Redis Object Cache - Version 1.3.8

Version Description

  • Switched from single file Predis version to full library
Download this release

Release Info

Developer tillkruess
Plugin Icon 128x128 Redis Object Cache
Version 1.3.8
Comparing to
See all releases

Code changes from version 1.3.7 to 1.3.8

Files changed (258) hide show
  1. includes/object-cache.php +2 -3
  2. includes/predis.php +0 -17359
  3. includes/predis/LICENSE +22 -0
  4. includes/predis/VERSION +1 -0
  5. includes/predis/autoload.php +14 -0
  6. includes/predis/src/Autoloader.php +62 -0
  7. includes/predis/src/Client.php +547 -0
  8. includes/predis/src/ClientContextInterface.php +198 -0
  9. includes/predis/src/ClientException.php +21 -0
  10. includes/predis/src/ClientInterface.php +239 -0
  11. includes/predis/src/Cluster/ClusterStrategy.php +469 -0
  12. includes/predis/src/Cluster/Distributor/DistributorInterface.php +82 -0
  13. includes/predis/src/Cluster/Distributor/EmptyRingException.php +21 -0
  14. includes/predis/src/Cluster/Distributor/HashRing.php +270 -0
  15. includes/predis/src/Cluster/Distributor/KetamaRing.php +71 -0
  16. includes/predis/src/Cluster/Hash/CRC16.php +72 -0
  17. includes/predis/src/Cluster/Hash/HashGeneratorInterface.php +30 -0
  18. includes/predis/src/Cluster/PredisStrategy.php +79 -0
  19. includes/predis/src/Cluster/RedisStrategy.php +58 -0
  20. includes/predis/src/Cluster/StrategyInterface.php +53 -0
  21. includes/predis/src/Collection/Iterator/CursorBasedIterator.php +191 -0
  22. includes/predis/src/Collection/Iterator/HashKey.php +60 -0
  23. includes/predis/src/Collection/Iterator/Keyspace.php +43 -0
  24. includes/predis/src/Collection/Iterator/ListKey.php +176 -0
  25. includes/predis/src/Collection/Iterator/SetKey.php +47 -0
  26. includes/predis/src/Collection/Iterator/SortedSetKey.php +60 -0
  27. includes/predis/src/Command/Command.php +129 -0
  28. includes/predis/src/Command/CommandInterface.php +81 -0
  29. includes/predis/src/Command/ConnectionAuth.php +28 -0
  30. includes/predis/src/Command/ConnectionEcho.php +28 -0
  31. includes/predis/src/Command/ConnectionPing.php +28 -0
  32. includes/predis/src/Command/ConnectionQuit.php +28 -0
  33. includes/predis/src/Command/ConnectionSelect.php +28 -0
  34. includes/predis/src/Command/GeospatialGeoAdd.php +42 -0
  35. includes/predis/src/Command/GeospatialGeoDist.php +28 -0
  36. includes/predis/src/Command/GeospatialGeoHash.php +41 -0
  37. includes/predis/src/Command/GeospatialGeoPos.php +41 -0
  38. includes/predis/src/Command/GeospatialGeoRadius.php +71 -0
  39. includes/predis/src/Command/GeospatialGeoRadiusByMember.php +28 -0
  40. includes/predis/src/Command/HashDelete.php +36 -0
  41. includes/predis/src/Command/HashExists.php +28 -0
  42. includes/predis/src/Command/HashGet.php +28 -0
  43. includes/predis/src/Command/HashGetAll.php +42 -0
  44. includes/predis/src/Command/HashGetMultiple.php +36 -0
  45. includes/predis/src/Command/HashIncrementBy.php +28 -0
  46. includes/predis/src/Command/HashIncrementByFloat.php +28 -0
  47. includes/predis/src/Command/HashKeys.php +28 -0
  48. includes/predis/src/Command/HashLength.php +28 -0
  49. includes/predis/src/Command/HashScan.php +85 -0
  50. includes/predis/src/Command/HashSet.php +28 -0
  51. includes/predis/src/Command/HashSetMultiple.php +48 -0
  52. includes/predis/src/Command/HashSetPreserve.php +28 -0
  53. includes/predis/src/Command/HashStringLength.php +28 -0
  54. includes/predis/src/Command/HashValues.php +28 -0
  55. includes/predis/src/Command/HyperLogLogAdd.php +36 -0
  56. includes/predis/src/Command/HyperLogLogCount.php +36 -0
  57. includes/predis/src/Command/HyperLogLogMerge.php +36 -0
  58. includes/predis/src/Command/KeyDelete.php +36 -0
  59. includes/predis/src/Command/KeyDump.php +28 -0
  60. includes/predis/src/Command/KeyExists.php +28 -0
  61. includes/predis/src/Command/KeyExpire.php +28 -0
  62. includes/predis/src/Command/KeyExpireAt.php +28 -0
  63. includes/predis/src/Command/KeyKeys.php +28 -0
  64. includes/predis/src/Command/KeyMigrate.php +50 -0
  65. includes/predis/src/Command/KeyMove.php +28 -0
  66. includes/predis/src/Command/KeyPersist.php +28 -0
  67. includes/predis/src/Command/KeyPreciseExpire.php +28 -0
  68. includes/predis/src/Command/KeyPreciseExpireAt.php +28 -0
  69. includes/predis/src/Command/KeyPreciseTimeToLive.php +28 -0
  70. includes/predis/src/Command/KeyRandom.php +36 -0
  71. includes/predis/src/Command/KeyRename.php +28 -0
  72. includes/predis/src/Command/KeyRenamePreserve.php +28 -0
  73. includes/predis/src/Command/KeyRestore.php +28 -0
  74. includes/predis/src/Command/KeyScan.php +66 -0
  75. includes/predis/src/Command/KeySort.php +83 -0
  76. includes/predis/src/Command/KeyTimeToLive.php +28 -0
  77. includes/predis/src/Command/KeyType.php +28 -0
  78. includes/predis/src/Command/ListIndex.php +28 -0
  79. includes/predis/src/Command/ListInsert.php +28 -0
  80. includes/predis/src/Command/ListLength.php +28 -0
  81. includes/predis/src/Command/ListPopFirst.php +28 -0
  82. includes/predis/src/Command/ListPopFirstBlocking.php +41 -0
  83. includes/predis/src/Command/ListPopLast.php +28 -0
  84. includes/predis/src/Command/ListPopLastBlocking.php +28 -0
  85. includes/predis/src/Command/ListPopLastPushHead.php +28 -0
  86. includes/predis/src/Command/ListPopLastPushHeadBlocking.php +28 -0
  87. includes/predis/src/Command/ListPushHead.php +28 -0
  88. includes/predis/src/Command/ListPushHeadX.php +28 -0
  89. includes/predis/src/Command/ListPushTail.php +36 -0
  90. includes/predis/src/Command/ListPushTailX.php +28 -0
  91. includes/predis/src/Command/ListRange.php +28 -0
  92. includes/predis/src/Command/ListRemove.php +28 -0
  93. includes/predis/src/Command/ListSet.php +28 -0
  94. includes/predis/src/Command/ListTrim.php +28 -0
  95. includes/predis/src/Command/PrefixableCommandInterface.php +27 -0
  96. includes/predis/src/Command/Processor/KeyPrefixProcessor.php +450 -0
  97. includes/predis/src/Command/Processor/ProcessorChain.php +130 -0
  98. includes/predis/src/Command/Processor/ProcessorInterface.php +29 -0
  99. includes/predis/src/Command/PubSubPublish.php +28 -0
  100. includes/predis/src/Command/PubSubPubsub.php +61 -0
  101. includes/predis/src/Command/PubSubSubscribe.php +36 -0
  102. includes/predis/src/Command/PubSubSubscribeByPattern.php +28 -0
  103. includes/predis/src/Command/PubSubUnsubscribe.php +36 -0
  104. includes/predis/src/Command/PubSubUnsubscribeByPattern.php +28 -0
  105. includes/predis/src/Command/RawCommand.php +131 -0
  106. includes/predis/src/Command/ScriptCommand.php +77 -0
  107. includes/predis/src/Command/ServerBackgroundRewriteAOF.php +36 -0
  108. includes/predis/src/Command/ServerBackgroundSave.php +36 -0
  109. includes/predis/src/Command/ServerClient.php +74 -0
  110. includes/predis/src/Command/ServerCommand.php +28 -0
  111. includes/predis/src/Command/ServerConfig.php +49 -0
  112. includes/predis/src/Command/ServerDatabaseSize.php +28 -0
  113. includes/predis/src/Command/ServerEval.php +38 -0
  114. includes/predis/src/Command/ServerEvalSHA.php +38 -0
  115. includes/predis/src/Command/ServerFlushAll.php +28 -0
  116. includes/predis/src/Command/ServerFlushDatabase.php +28 -0
  117. includes/predis/src/Command/ServerInfo.php +111 -0
  118. includes/predis/src/Command/ServerInfoV26x.php +56 -0
  119. includes/predis/src/Command/ServerLastSave.php +28 -0
  120. includes/predis/src/Command/ServerMonitor.php +28 -0
  121. includes/predis/src/Command/ServerObject.php +28 -0
  122. includes/predis/src/Command/ServerSave.php +28 -0
  123. includes/predis/src/Command/ServerScript.php +28 -0
  124. includes/predis/src/Command/ServerSentinel.php +66 -0
  125. includes/predis/src/Command/ServerShutdown.php +28 -0
  126. includes/predis/src/Command/ServerSlaveOf.php +40 -0
  127. includes/predis/src/Command/ServerSlowlog.php +51 -0
  128. includes/predis/src/Command/ServerTime.php +28 -0
  129. includes/predis/src/Command/SetAdd.php +36 -0
  130. includes/predis/src/Command/SetCardinality.php +28 -0
  131. includes/predis/src/Command/SetDifference.php +28 -0
  132. includes/predis/src/Command/SetDifferenceStore.php +28 -0
  133. includes/predis/src/Command/SetIntersection.php +36 -0
  134. includes/predis/src/Command/SetIntersectionStore.php +40 -0
  135. includes/predis/src/Command/SetIsMember.php +28 -0
  136. includes/predis/src/Command/SetMembers.php +28 -0
  137. includes/predis/src/Command/SetMove.php +28 -0
  138. includes/predis/src/Command/SetPop.php +28 -0
  139. includes/predis/src/Command/SetRandomMember.php +28 -0
  140. includes/predis/src/Command/SetRemove.php +36 -0
  141. includes/predis/src/Command/SetScan.php +66 -0
  142. includes/predis/src/Command/SetUnion.php +28 -0
  143. includes/predis/src/Command/SetUnionStore.php +28 -0
  144. includes/predis/src/Command/StringAppend.php +28 -0
  145. includes/predis/src/Command/StringBitCount.php +28 -0
  146. includes/predis/src/Command/StringBitField.php +28 -0
  147. includes/predis/src/Command/StringBitOp.php +42 -0
  148. includes/predis/src/Command/StringBitPos.php +28 -0
  149. includes/predis/src/Command/StringDecrement.php +28 -0
  150. includes/predis/src/Command/StringDecrementBy.php +28 -0
  151. includes/predis/src/Command/StringGet.php +28 -0
  152. includes/predis/src/Command/StringGetBit.php +28 -0
  153. includes/predis/src/Command/StringGetMultiple.php +36 -0
  154. includes/predis/src/Command/StringGetRange.php +28 -0
  155. includes/predis/src/Command/StringGetSet.php +28 -0
  156. includes/predis/src/Command/StringIncrement.php +28 -0
  157. includes/predis/src/Command/StringIncrementBy.php +28 -0
  158. includes/predis/src/Command/StringIncrementByFloat.php +28 -0
  159. includes/predis/src/Command/StringPreciseSetExpire.php +28 -0
  160. includes/predis/src/Command/StringSet.php +28 -0
  161. includes/predis/src/Command/StringSetBit.php +28 -0
  162. includes/predis/src/Command/StringSetExpire.php +28 -0
  163. includes/predis/src/Command/StringSetMultiple.php +48 -0
  164. includes/predis/src/Command/StringSetMultiplePreserve.php +28 -0
  165. includes/predis/src/Command/StringSetPreserve.php +28 -0
  166. includes/predis/src/Command/StringSetRange.php +28 -0
  167. includes/predis/src/Command/StringStrlen.php +28 -0
  168. includes/predis/src/Command/StringSubstr.php +28 -0
  169. includes/predis/src/Command/TransactionDiscard.php +28 -0
  170. includes/predis/src/Command/TransactionExec.php +28 -0
  171. includes/predis/src/Command/TransactionMulti.php +28 -0
  172. includes/predis/src/Command/TransactionUnwatch.php +28 -0
  173. includes/predis/src/Command/TransactionWatch.php +40 -0
  174. includes/predis/src/Command/ZSetAdd.php +43 -0
  175. includes/predis/src/Command/ZSetCardinality.php +28 -0
  176. includes/predis/src/Command/ZSetCount.php +28 -0
  177. includes/predis/src/Command/ZSetIncrementBy.php +28 -0
  178. includes/predis/src/Command/ZSetIntersectionStore.php +28 -0
  179. includes/predis/src/Command/ZSetLexCount.php +28 -0
  180. includes/predis/src/Command/ZSetRange.php +105 -0
  181. includes/predis/src/Command/ZSetRangeByLex.php +55 -0
  182. includes/predis/src/Command/ZSetRangeByScore.php +68 -0
  183. includes/predis/src/Command/ZSetRank.php +28 -0
  184. includes/predis/src/Command/ZSetRemove.php +36 -0
  185. includes/predis/src/Command/ZSetRemoveRangeByLex.php +28 -0
  186. includes/predis/src/Command/ZSetRemoveRangeByRank.php +28 -0
  187. includes/predis/src/Command/ZSetRemoveRangeByScore.php +28 -0
  188. includes/predis/src/Command/ZSetReverseRange.php +28 -0
  189. includes/predis/src/Command/ZSetReverseRangeByLex.php +28 -0
  190. includes/predis/src/Command/ZSetReverseRangeByScore.php +28 -0
  191. includes/predis/src/Command/ZSetReverseRank.php +28 -0
  192. includes/predis/src/Command/ZSetScan.php +85 -0
  193. includes/predis/src/Command/ZSetScore.php +28 -0
  194. includes/predis/src/Command/ZSetUnionStore.php +78 -0
  195. includes/predis/src/CommunicationException.php +80 -0
  196. includes/predis/src/Configuration/ClusterOption.php +76 -0
  197. includes/predis/src/Configuration/ConnectionFactoryOption.php +60 -0
  198. includes/predis/src/Configuration/ExceptionsOption.php +37 -0
  199. includes/predis/src/Configuration/OptionInterface.php +40 -0
  200. includes/predis/src/Configuration/Options.php +122 -0
  201. includes/predis/src/Configuration/OptionsInterface.php +64 -0
  202. includes/predis/src/Configuration/PrefixOption.php +44 -0
  203. includes/predis/src/Configuration/ProfileOption.php +69 -0
  204. includes/predis/src/Configuration/ReplicationOption.php +75 -0
  205. includes/predis/src/Connection/AbstractConnection.php +226 -0
  206. includes/predis/src/Connection/Aggregate/ClusterInterface.php +24 -0
  207. includes/predis/src/Connection/Aggregate/MasterSlaveReplication.php +509 -0
  208. includes/predis/src/Connection/Aggregate/PredisCluster.php +235 -0
  209. includes/predis/src/Connection/Aggregate/RedisCluster.php +673 -0
  210. includes/predis/src/Connection/Aggregate/ReplicationInterface.php +52 -0
  211. includes/predis/src/Connection/Aggregate/SentinelReplication.php +720 -0
  212. includes/predis/src/Connection/AggregateConnectionInterface.php +57 -0
  213. includes/predis/src/Connection/CompositeConnectionInterface.php +49 -0
  214. includes/predis/src/Connection/CompositeStreamConnection.php +125 -0
  215. includes/predis/src/Connection/ConnectionException.php +23 -0
  216. includes/predis/src/Connection/ConnectionInterface.php +66 -0
  217. includes/predis/src/Connection/Factory.php +188 -0
  218. includes/predis/src/Connection/FactoryInterface.php +52 -0
  219. includes/predis/src/Connection/NodeConnectionInterface.php +58 -0
  220. includes/predis/src/Connection/Parameters.php +176 -0
  221. includes/predis/src/Connection/ParametersInterface.php +62 -0
  222. includes/predis/src/Connection/PhpiredisSocketConnection.php +418 -0
  223. includes/predis/src/Connection/PhpiredisStreamConnection.php +238 -0
  224. includes/predis/src/Connection/StreamConnection.php +396 -0
  225. includes/predis/src/Connection/WebdisConnection.php +366 -0
  226. includes/predis/src/Monitor/Consumer.php +173 -0
  227. includes/predis/src/NotSupportedException.php +22 -0
  228. includes/predis/src/Pipeline/Atomic.php +119 -0
  229. includes/predis/src/Pipeline/ConnectionErrorProof.php +130 -0
  230. includes/predis/src/Pipeline/FireAndForget.php +36 -0
  231. includes/predis/src/Pipeline/Pipeline.php +247 -0
  232. includes/predis/src/PredisException.php +21 -0
  233. includes/predis/src/Profile/Factory.php +101 -0
  234. includes/predis/src/Profile/ProfileInterface.php +59 -0
  235. includes/predis/src/Profile/RedisProfile.php +146 -0
  236. includes/predis/src/Profile/RedisUnstable.php +38 -0
  237. includes/predis/src/Profile/RedisVersion200.php +173 -0
  238. includes/predis/src/Profile/RedisVersion220.php +202 -0
  239. includes/predis/src/Profile/RedisVersion240.php +207 -0
  240. includes/predis/src/Profile/RedisVersion260.php +235 -0
  241. includes/predis/src/Profile/RedisVersion280.php +267 -0
  242. includes/predis/src/Profile/RedisVersion300.php +270 -0
  243. includes/predis/src/Profile/RedisVersion320.php +281 -0
  244. includes/predis/src/Protocol/ProtocolException.php +24 -0
  245. includes/predis/src/Protocol/ProtocolProcessorInterface.php +41 -0
  246. includes/predis/src/Protocol/RequestSerializerInterface.php +31 -0
  247. includes/predis/src/Protocol/ResponseReaderInterface.php +32 -0
  248. includes/predis/src/Protocol/Text/CompositeProtocolProcessor.php +107 -0
  249. includes/predis/src/Protocol/Text/Handler/BulkResponse.php +55 -0
  250. includes/predis/src/Protocol/Text/Handler/ErrorResponse.php +34 -0
  251. includes/predis/src/Protocol/Text/Handler/IntegerResponse.php +46 -0
  252. includes/predis/src/Protocol/Text/Handler/MultiBulkResponse.php +68 -0
  253. includes/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php +33 -0
  254. includes/predis/src/Protocol/Text/Handler/StatusResponse.php +35 -0
  255. includes/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php +47 -0
  256. includes/predis/src/Protocol/Text/ProtocolProcessor.php +123 -0
  257. includes/predis/src/Protocol/Text/RequestSerializer.php +46 -0
  258. includes/predis/src/Protocol/Text/ResponseReader.php +89 -0
includes/object-cache.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Redis Object Cache Drop-In
4
  Plugin URI: http://wordpress.org/plugins/redis-cache/
5
  Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, HHVM, replication, clustering and WP-CLI.
6
- Version: 1.3.7
7
  Author: Till Krüss
8
  Author URI: https://till.im/
9
  License: GPLv3
@@ -459,8 +459,7 @@ class WP_Object_Cache
459
  // Load bundled Predis library
460
  if (! class_exists('Predis\Client')) {
461
  $plugin_dir = defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins';
462
- require_once $plugin_dir . '/redis-cache/includes/predis.php';
463
- Predis\Autoloader::register();
464
  }
465
 
466
  $options = array();
3
  Plugin Name: Redis Object Cache Drop-In
4
  Plugin URI: http://wordpress.org/plugins/redis-cache/
5
  Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, HHVM, replication, clustering and WP-CLI.
6
+ Version: 1.3.8
7
  Author: Till Krüss
8
  Author URI: https://till.im/
9
  License: GPLv3
459
  // Load bundled Predis library
460
  if (! class_exists('Predis\Client')) {
461
  $plugin_dir = defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins';
462
+ require_once $plugin_dir . '/redis-cache/includes/predis/autoload.php';
 
463
  }
464
 
465
  $options = array();
includes/predis.php DELETED
@@ -1,17359 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Predis package.
5
- *
6
- * (c) Daniele Alessandri <suppakilla@gmail.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Predis\Command;
13
-
14
- /**
15
- * Defines an abstraction representing a Redis command.
16
- *
17
- * @author Daniele Alessandri <suppakilla@gmail.com>
18
- */
19
- interface CommandInterface
20
- {
21
- /**
22
- * Returns the ID of the Redis command. By convention, command identifiers
23
- * must always be uppercase.
24
- *
25
- * @return string
26
- */
27
- public function getId();
28
-
29
- /**
30
- * Assign the specified slot to the command for clustering distribution.
31
- *
32
- * @param int $slot Slot ID.
33
- */
34
- public function setSlot($slot);
35
-
36
- /**
37
- * Returns the assigned slot of the command for clustering distribution.
38
- *
39
- * @return int|null
40
- */
41
- public function getSlot();
42
-
43
- /**
44
- * Sets the arguments for the command.
45
- *
46
- * @param array $arguments List of arguments.
47
- */
48
- public function setArguments(array $arguments);
49
-
50
- /**
51
- * Sets the raw arguments for the command without processing them.
52
- *
53
- * @param array $arguments List of arguments.
54
- */
55
- public function setRawArguments(array $arguments);
56
-
57
- /**
58
- * Gets the arguments of the command.
59
- *
60
- * @return array
61
- */
62
- public function getArguments();
63
-
64
- /**
65
- * Gets the argument of the command at the specified index.
66
- *
67
- * @param int $index Index of the desired argument.
68
- *
69
- * @return mixed|null
70
- */
71
- public function getArgument($index);
72
-
73
- /**
74
- * Parses a raw response and returns a PHP object.
75
- *
76
- * @param string $data Binary string containing the whole response.
77
- *
78
- * @return mixed
79
- */
80
- public function parseResponse($data);
81
- }
82
-
83
- /**
84
- * Base class for Redis commands.
85
- *
86
- * @author Daniele Alessandri <suppakilla@gmail.com>
87
- */
88
- abstract class Command implements CommandInterface
89
- {
90
- private $slot;
91
- private $arguments = array();
92
-
93
- /**
94
- * Returns a filtered array of the arguments.
95
- *
96
- * @param array $arguments List of arguments.
97
- *
98
- * @return array
99
- */
100
- protected function filterArguments(array $arguments)
101
- {
102
- return $arguments;
103
- }
104
-
105
- /**
106
- * {@inheritdoc}
107
- */
108
- public function setArguments(array $arguments)
109
- {
110
- $this->arguments = $this->filterArguments($arguments);
111
- unset($this->slot);
112
- }
113
-
114
- /**
115
- * {@inheritdoc}
116
- */
117
- public function setRawArguments(array $arguments)
118
- {
119
- $this->arguments = $arguments;
120
- unset($this->slot);
121
- }
122
-
123
- /**
124
- * {@inheritdoc}
125
- */
126
- public function getArguments()
127
- {
128
- return $this->arguments;
129
- }
130
-
131
- /**
132
- * {@inheritdoc}
133
- */
134
- public function getArgument($index)
135
- {
136
- if (isset($this->arguments[$index])) {
137
- return $this->arguments[$index];
138
- }
139
- }
140
-
141
- /**
142
- * {@inheritdoc}
143
- */
144
- public function setSlot($slot)
145
- {
146
- $this->slot = $slot;
147
- }
148
-
149
- /**
150
- * {@inheritdoc}
151
- */
152
- public function getSlot()
153
- {
154
- if (isset($this->slot)) {
155
- return $this->slot;
156
- }
157
- }
158
-
159
- /**
160
- * {@inheritdoc}
161
- */
162
- public function parseResponse($data)
163
- {
164
- return $data;
165
- }
166
-
167
- /**
168
- * Normalizes the arguments array passed to a Redis command.
169
- *
170
- * @param array $arguments Arguments for a command.
171
- *
172
- * @return array
173
- */
174
- public static function normalizeArguments(array $arguments)
175
- {
176
- if (count($arguments) === 1 && is_array($arguments[0])) {
177
- return $arguments[0];
178
- }
179
-
180
- return $arguments;
181
- }
182
-
183
- /**
184
- * Normalizes the arguments array passed to a variadic Redis command.
185
- *
186
- * @param array $arguments Arguments for a command.
187
- *
188
- * @return array
189
- */
190
- public static function normalizeVariadic(array $arguments)
191
- {
192
- if (count($arguments) === 2 && is_array($arguments[1])) {
193
- return array_merge(array($arguments[0]), $arguments[1]);
194
- }
195
-
196
- return $arguments;
197
- }
198
- }
199
-
200
- /**
201
- * @link http://redis.io/commands/zrange
202
- *
203
- * @author Daniele Alessandri <suppakilla@gmail.com>
204
- */
205
- class ZSetRange extends Command
206
- {
207
- /**
208
- * {@inheritdoc}
209
- */
210
- public function getId()
211
- {
212
- return 'ZRANGE';
213
- }
214
-
215
- /**
216
- * {@inheritdoc}
217
- */
218
- protected function filterArguments(array $arguments)
219
- {
220
- if (count($arguments) === 4) {
221
- $lastType = gettype($arguments[3]);
222
-
223
- if ($lastType === 'string' && strtoupper($arguments[3]) === 'WITHSCORES') {
224
- // Used for compatibility with older versions
225
- $arguments[3] = array('WITHSCORES' => true);
226
- $lastType = 'array';
227
- }
228
-
229
- if ($lastType === 'array') {
230
- $options = $this->prepareOptions(array_pop($arguments));
231
-
232
- return array_merge($arguments, $options);
233
- }
234
- }
235
-
236
- return $arguments;
237
- }
238
-
239
- /**
240
- * Returns a list of options and modifiers compatible with Redis.
241
- *
242
- * @param array $options List of options.
243
- *
244
- * @return array
245
- */
246
- protected function prepareOptions($options)
247
- {
248
- $opts = array_change_key_case($options, CASE_UPPER);
249
- $finalizedOpts = array();
250
-
251
- if (!empty($opts['WITHSCORES'])) {
252
- $finalizedOpts[] = 'WITHSCORES';
253
- }
254
-
255
- return $finalizedOpts;
256
- }
257
-
258
- /**
259
- * Checks for the presence of the WITHSCORES modifier.
260
- *
261
- * @return bool
262
- */
263
- protected function withScores()
264
- {
265
- $arguments = $this->getArguments();
266
-
267
- if (count($arguments) < 4) {
268
- return false;
269
- }
270
-
271
- return strtoupper($arguments[3]) === 'WITHSCORES';
272
- }
273
-
274
- /**
275
- * {@inheritdoc}
276
- */
277
- public function parseResponse($data)
278
- {
279
- if ($this->withScores()) {
280
- $result = array();
281
-
282
- for ($i = 0; $i < count($data); ++$i) {
283
- $result[$data[$i]] = $data[++$i];
284
- }
285
-
286
- return $result;
287
- }
288
-
289
- return $data;
290
- }
291
- }
292
-
293
- /**
294
- * @link http://redis.io/commands/sinterstore
295
- *
296
- * @author Daniele Alessandri <suppakilla@gmail.com>
297
- */
298
- class SetIntersectionStore extends Command
299
- {
300
- /**
301
- * {@inheritdoc}
302
- */
303
- public function getId()
304
- {
305
- return 'SINTERSTORE';
306
- }
307
-
308
- /**
309
- * {@inheritdoc}
310
- */
311
- protected function filterArguments(array $arguments)
312
- {
313
- if (count($arguments) === 2 && is_array($arguments[1])) {
314
- return array_merge(array($arguments[0]), $arguments[1]);
315
- }
316
-
317
- return $arguments;
318
- }
319
- }
320
-
321
- /**
322
- * @link http://redis.io/commands/sinter
323
- *
324
- * @author Daniele Alessandri <suppakilla@gmail.com>
325
- */
326
- class SetIntersection extends Command
327
- {
328
- /**
329
- * {@inheritdoc}
330
- */
331
- public function getId()
332
- {
333
- return 'SINTER';
334
- }
335
-
336
- /**
337
- * {@inheritdoc}
338
- */
339
- protected function filterArguments(array $arguments)
340
- {
341
- return self::normalizeArguments($arguments);
342
- }
343
- }
344
-
345
- /**
346
- * @link http://redis.io/commands/eval
347
- *
348
- * @author Daniele Alessandri <suppakilla@gmail.com>
349
- */
350
- class ServerEval extends Command
351
- {
352
- /**
353
- * {@inheritdoc}
354
- */
355
- public function getId()
356
- {
357
- return 'EVAL';
358
- }
359
-
360
- /**
361
- * Calculates the SHA1 hash of the body of the script.
362
- *
363
- * @return string SHA1 hash.
364
- */
365
- public function getScriptHash()
366
- {
367
- return sha1($this->getArgument(0));
368
- }
369
- }
370
-
371
- /**
372
- * @link http://redis.io/commands/zunionstore
373
- *
374
- * @author Daniele Alessandri <suppakilla@gmail.com>
375
- */
376
- class ZSetUnionStore extends Command
377
- {
378
- /**
379
- * {@inheritdoc}
380
- */
381
- public function getId()
382
- {
383
- return 'ZUNIONSTORE';
384
- }
385
-
386
- /**
387
- * {@inheritdoc}
388
- */
389
- protected function filterArguments(array $arguments)
390
- {
391
- $options = array();
392
- $argc = count($arguments);
393
-
394
- if ($argc > 2 && is_array($arguments[$argc - 1])) {
395
- $options = $this->prepareOptions(array_pop($arguments));
396
- }
397
-
398
- if (is_array($arguments[1])) {
399
- $arguments = array_merge(
400
- array($arguments[0], count($arguments[1])),
401
- $arguments[1]
402
- );
403
- }
404
-
405
- return array_merge($arguments, $options);
406
- }
407
-
408
- /**
409
- * Returns a list of options and modifiers compatible with Redis.
410
- *
411
- * @param array $options List of options.
412
- *
413
- * @return array
414
- */
415
- private function prepareOptions($options)
416
- {
417
- $opts = array_change_key_case($options, CASE_UPPER);
418
- $finalizedOpts = array();
419
-
420
- if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) {
421
- $finalizedOpts[] = 'WEIGHTS';
422
-
423
- foreach ($opts['WEIGHTS'] as $weight) {
424
- $finalizedOpts[] = $weight;
425
- }
426
- }
427
-
428
- if (isset($opts['AGGREGATE'])) {
429
- $finalizedOpts[] = 'AGGREGATE';
430
- $finalizedOpts[] = $opts['AGGREGATE'];
431
- }
432
-
433
- return $finalizedOpts;
434
- }
435
- }
436
-
437
- /**
438
- * @link http://redis.io/commands/georadius
439
- *
440
- * @author Daniele Alessandri <suppakilla@gmail.com>
441
- */
442
- class GeospatialGeoRadius extends Command
443
- {
444
- /**
445
- * {@inheritdoc}
446
- */
447
- public function getId()
448
- {
449
- return 'GEORADIUS';
450
- }
451
-
452
- /**
453
- * {@inheritdoc}
454
- */
455
- protected function filterArguments(array $arguments)
456
- {
457
- if ($arguments && is_array(end($arguments))) {
458
- $options = array_change_key_case(array_pop($arguments), CASE_UPPER);
459
-
460
- if (isset($options['WITHCOORD']) && $options['WITHCOORD'] == true) {
461
- $arguments[] = 'WITHCOORD';
462
- }
463
-
464
- if (isset($options['WITHDIST']) && $options['WITHDIST'] == true) {
465
- $arguments[] = 'WITHDIST';
466
- }
467
-
468
- if (isset($options['WITHHASH']) && $options['WITHHASH'] == true) {
469
- $arguments[] = 'WITHHASH';
470
- }
471
-
472
- if (isset($options['COUNT'])) {
473
- $arguments[] = 'COUNT';
474
- $arguments[] = $options['COUNT'];
475
- }
476
-
477
- if (isset($options['SORT'])) {
478
- $arguments[] = strtoupper($options['SORT']);
479
- }
480
-
481
- if (isset($options['STORE'])) {
482
- $arguments[] = 'STORE';
483
- $arguments[] = $options['STORE'];
484
- }
485
-
486
- if (isset($options['STOREDIST'])) {
487
- $arguments[] = 'STOREDIST';
488
- $arguments[] = $options['STOREDIST'];
489
- }
490
- }
491
-
492
- return $arguments;
493
- }
494
- }
495
-
496
- /**
497
- * @link http://redis.io/commands/zrangebylex
498
- *
499
- * @author Daniele Alessandri <suppakilla@gmail.com>
500
- */
501
- class ZSetRangeByLex extends ZSetRange
502
- {
503
- /**
504
- * {@inheritdoc}
505
- */
506
- public function getId()
507
- {
508
- return 'ZRANGEBYLEX';
509
- }
510
-
511
- /**
512
- * {@inheritdoc}
513
- */
514
- protected function prepareOptions($options)
515
- {
516
- $opts = array_change_key_case($options, CASE_UPPER);
517
- $finalizedOpts = array();
518
-
519
- if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
520
- $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);
521
-
522
- $finalizedOpts[] = 'LIMIT';
523
- $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0];
524
- $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1];
525
- }
526
-
527
- return $finalizedOpts;
528
- }
529
-
530
- /**
531
- * {@inheritdoc}
532
- */
533
- protected function withScores()
534
- {
535
- return false;
536
- }
537
- }
538
-
539
- /**
540
- * @link http://redis.io/commands/subscribe
541
- *
542
- * @author Daniele Alessandri <suppakilla@gmail.com>
543
- */
544
- class PubSubSubscribe extends Command
545
- {
546
- /**
547
- * {@inheritdoc}
548
- */
549
- public function getId()
550
- {
551
- return 'SUBSCRIBE';
552
- }
553
-
554
- /**
555
- * {@inheritdoc}
556
- */
557
- protected function filterArguments(array $arguments)
558
- {
559
- return self::normalizeArguments($arguments);
560
- }
561
- }
562
-
563
- /**
564
- * @link http://redis.io/commands/info
565
- *
566
- * @author Daniele Alessandri <suppakilla@gmail.com>
567
- */
568
- class ServerInfo extends Command
569
- {
570
- /**
571
- * {@inheritdoc}
572
- */
573
- public function getId()
574
- {
575
- return 'INFO';
576
- }
577
-
578
- /**
579
- * {@inheritdoc}
580
- */
581
- public function parseResponse($data)
582
- {
583
- $info = array();
584
- $infoLines = preg_split('/\r?\n/', $data);
585
-
586
- foreach ($infoLines as $row) {
587
- if (strpos($row, ':') === false) {
588
- continue;
589
- }
590
-
591
- list($k, $v) = $this->parseRow($row);
592
- $info[$k] = $v;
593
- }
594
-
595
- return $info;
596
- }
597
-
598
- /**
599
- * Parses a single row of the response and returns the key-value pair.
600
- *
601
- * @param string $row Single row of the response.
602
- *
603
- * @return array
604
- */
605
- protected function parseRow($row)
606
- {
607
- list($k, $v) = explode(':', $row, 2);
608
-
609
- if (preg_match('/^db\d+$/', $k)) {
610
- $v = $this->parseDatabaseStats($v);
611
- }
612
-
613
- return array($k, $v);
614
- }
615
-
616
- /**
617
- * Extracts the statistics of each logical DB from the string buffer.
618
- *
619
- * @param string $str Response buffer.
620
- *
621
- * @return array
622
- */
623
- protected function parseDatabaseStats($str)
624
- {
625
- $db = array();
626
-
627
- foreach (explode(',', $str) as $dbvar) {
628
- list($dbvk, $dbvv) = explode('=', $dbvar);
629
- $db[trim($dbvk)] = $dbvv;
630
- }
631
-
632
- return $db;
633
- }
634
-
635
- /**
636
- * Parses the response and extracts the allocation statistics.
637
- *
638
- * @param string $str Response buffer.
639
- *
640
- * @return array
641
- */
642
- protected function parseAllocationStats($str)
643
- {
644
- $stats = array();
645
-
646
- foreach (explode(',', $str) as $kv) {
647
- @list($size, $objects, $extra) = explode('=', $kv);
648
-
649
- // hack to prevent incorrect values when parsing the >=256 key
650
- if (isset($extra)) {
651
- $size = ">=$objects";
652
- $objects = $extra;
653
- }
654
-
655
- $stats[$size] = $objects;
656
- }
657
-
658
- return $stats;
659
- }
660
- }
661
-
662
- /**
663
- * @link http://redis.io/commands/expireat
664
- *
665
- * @author Daniele Alessandri <suppakilla@gmail.com>
666
- */
667
- class KeyExpireAt extends Command
668
- {
669
- /**
670
- * {@inheritdoc}
671
- */
672
- public function getId()
673
- {
674
- return 'EXPIREAT';
675
- }
676
- }
677
-
678
- /**
679
- * @link http://redis.io/commands/unsubscribe
680
- *
681
- * @author Daniele Alessandri <suppakilla@gmail.com>
682
- */
683
- class PubSubUnsubscribe extends Command
684
- {
685
- /**
686
- * {@inheritdoc}
687
- */
688
- public function getId()
689
- {
690
- return 'UNSUBSCRIBE';
691
- }
692
-
693
- /**
694
- * {@inheritdoc}
695
- */
696
- protected function filterArguments(array $arguments)
697
- {
698
- return self::normalizeArguments($arguments);
699
- }
700
- }
701
-
702
- /**
703
- * @link http://redis.io/commands/evalsha
704
- *
705
- * @author Daniele Alessandri <suppakilla@gmail.com>
706
- */
707
- class ServerEvalSHA extends ServerEval
708
- {
709
- /**
710
- * {@inheritdoc}
711
- */
712
- public function getId()
713
- {
714
- return 'EVALSHA';
715
- }
716
-
717
- /**
718
- * Returns the SHA1 hash of the body of the script.
719
- *
720
- * @return string SHA1 hash.
721
- */
722
- public function getScriptHash()
723
- {
724
- return $this->getArgument(0);
725
- }
726
- }
727
-
728
- /**
729
- * @link http://redis.io/commands/ttl
730
- *
731
- * @author Daniele Alessandri <suppakilla@gmail.com>
732
- */
733
- class KeyTimeToLive extends Command
734
- {
735
- /**
736
- * {@inheritdoc}
737
- */
738
- public function getId()
739
- {
740
- return 'TTL';
741
- }
742
- }
743
-
744
- /**
745
- * @link http://redis.io/commands/setex
746
- *
747
- * @author Daniele Alessandri <suppakilla@gmail.com>
748
- */
749
- class StringSetExpire extends Command
750
- {
751
- /**
752
- * {@inheritdoc}
753
- */
754
- public function getId()
755
- {
756
- return 'SETEX';
757
- }
758
- }
759
-
760
- /**
761
- * @link http://redis.io/commands/mset
762
- *
763
- * @author Daniele Alessandri <suppakilla@gmail.com>
764
- */
765
- class StringSetMultiple extends Command
766
- {
767
- /**
768
- * {@inheritdoc}
769
- */
770
- public function getId()
771
- {
772
- return 'MSET';
773
- }
774
-
775
- /**
776
- * {@inheritdoc}
777
- */
778
- protected function filterArguments(array $arguments)
779
- {
780
- if (count($arguments) === 1 && is_array($arguments[0])) {
781
- $flattenedKVs = array();
782
- $args = $arguments[0];
783
-
784
- foreach ($args as $k => $v) {
785
- $flattenedKVs[] = $k;
786
- $flattenedKVs[] = $v;
787
- }
788
-
789
- return $flattenedKVs;
790
- }
791
-
792
- return $arguments;
793
- }
794
- }
795
-
796
- /**
797
- * @link http://redis.io/commands/zrangebyscore
798
- *
799
- * @author Daniele Alessandri <suppakilla@gmail.com>
800
- */
801
- class ZSetRangeByScore extends ZSetRange
802
- {
803
- /**
804
- * {@inheritdoc}
805
- */
806
- public function getId()
807
- {
808
- return 'ZRANGEBYSCORE';
809
- }
810
-
811
- /**
812
- * {@inheritdoc}
813
- */
814
- protected function prepareOptions($options)
815
- {
816
- $opts = array_change_key_case($options, CASE_UPPER);
817
- $finalizedOpts = array();
818
-
819
- if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
820
- $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);
821
-
822
- $finalizedOpts[] = 'LIMIT';
823
- $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0];
824
- $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1];
825
- }
826
-
827
- return array_merge($finalizedOpts, parent::prepareOptions($options));
828
- }
829
-
830
- /**
831
- * {@inheritdoc}
832
- */
833
- protected function withScores()
834
- {
835
- $arguments = $this->getArguments();
836
-
837
- for ($i = 3; $i < count($arguments); ++$i) {
838
- switch (strtoupper($arguments[$i])) {
839
- case 'WITHSCORES':
840
- return true;
841
-
842
- case 'LIMIT':
843
- $i += 2;
844
- break;
845
- }
846
- }
847
-
848
- return false;
849
- }
850
- }
851
-
852
- /**
853
- * @link http://redis.io/commands/blpop
854
- *
855
- * @author Daniele Alessandri <suppakilla@gmail.com>
856
- */
857
- class ListPopFirstBlocking extends Command
858
- {
859
- /**
860
- * {@inheritdoc}
861
- */
862
- public function getId()
863
- {
864
- return 'BLPOP';
865
- }
866
-
867
- /**
868
- * {@inheritdoc}
869
- */
870
- protected function filterArguments(array $arguments)
871
- {
872
- if (count($arguments) === 2 && is_array($arguments[0])) {
873
- list($arguments, $timeout) = $arguments;
874
- array_push($arguments, $timeout);
875
- }
876
-
877
- return $arguments;
878
- }
879
- }
880
-
881
- /**
882
- * @link http://redis.io/commands/rpush
883
- *
884
- * @author Daniele Alessandri <suppakilla@gmail.com>
885
- */
886
- class ListPushTail extends Command
887
- {
888
- /**
889
- * {@inheritdoc}
890
- */
891
- public function getId()
892
- {
893
- return 'RPUSH';
894
- }
895
-
896
- /**
897
- * {@inheritdoc}
898
- */
899
- protected function filterArguments(array $arguments)
900
- {
901
- return self::normalizeVariadic($arguments);
902
- }
903
- }
904
-
905
- /**
906
- * @link http://redis.io/commands/rename
907
- *
908
- * @author Daniele Alessandri <suppakilla@gmail.com>
909
- */
910
- class KeyRename extends Command
911
- {
912
- /**
913
- * {@inheritdoc}
914
- */
915
- public function getId()
916
- {
917
- return 'RENAME';
918
- }
919
- }
920
-
921
- /**
922
- * @link http://redis.io/commands/expire
923
- *
924
- * @author Daniele Alessandri <suppakilla@gmail.com>
925
- */
926
- class KeyExpire extends Command
927
- {
928
- /**
929
- * {@inheritdoc}
930
- */
931
- public function getId()
932
- {
933
- return 'EXPIRE';
934
- }
935
- }
936
-
937
- /**
938
- * @link http://redis.io/commands/move
939
- *
940
- * @author Daniele Alessandri <suppakilla@gmail.com>
941
- */
942
- class KeyMove extends Command
943
- {
944
- /**
945
- * {@inheritdoc}
946
- */
947
- public function getId()
948
- {
949
- return 'MOVE';
950
- }
951
- }
952
-
953
- /**
954
- * @link http://redis.io/commands/sadd
955
- *
956
- * @author Daniele Alessandri <suppakilla@gmail.com>
957
- */
958
- class SetAdd extends Command
959
- {
960
- /**
961
- * {@inheritdoc}
962
- */
963
- public function getId()
964
- {
965
- return 'SADD';
966
- }
967
-
968
- /**
969
- * {@inheritdoc}
970
- */
971
- protected function filterArguments(array $arguments)
972
- {
973
- return self::normalizeVariadic($arguments);
974
- }
975
- }
976
-
977
- /**
978
- * @link http://redis.io/commands/append
979
- *
980
- * @author Daniele Alessandri <suppakilla@gmail.com>
981
- */
982
- class StringAppend extends Command
983
- {
984
- /**
985
- * {@inheritdoc}
986
- */
987
- public function getId()
988
- {
989
- return 'APPEND';
990
- }
991
- }
992
-
993
- /**
994
- * @link http://redis.io/commands/lindex
995
- *
996
- * @author Daniele Alessandri <suppakilla@gmail.com>
997
- */
998
- class ListIndex extends Command
999
- {
1000
- /**
1001
- * {@inheritdoc}
1002
- */
1003
- public function getId()
1004
- {
1005
- return 'LINDEX';
1006
- }
1007
- }
1008
-
1009
- /**
1010
- * @link http://redis.io/commands/psetex
1011
- *
1012
- * @author Daniele Alessandri <suppakilla@gmail.com>
1013
- */
1014
- class StringPreciseSetExpire extends StringSetExpire
1015
- {
1016
- /**
1017
- * {@inheritdoc}
1018
- */
1019
- public function getId()
1020
- {
1021
- return 'PSETEX';
1022
- }
1023
- }
1024
-
1025
- /**
1026
- * @link http://redis.io/commands/save
1027
- *
1028
- * @author Daniele Alessandri <suppakilla@gmail.com>
1029
- */
1030
- class ServerSave extends Command
1031
- {
1032
- /**
1033
- * {@inheritdoc}
1034
- */
1035
- public function getId()
1036
- {
1037
- return 'SAVE';
1038
- }
1039
- }
1040
-
1041
- /**
1042
- * @link http://redis.io/commands/llen
1043
- *
1044
- * @author Daniele Alessandri <suppakilla@gmail.com>
1045
- */
1046
- class ListLength extends Command
1047
- {
1048
- /**
1049
- * {@inheritdoc}
1050
- */
1051
- public function getId()
1052
- {
1053
- return 'LLEN';
1054
- }
1055
- }
1056
-
1057
- /**
1058
- * @link http://redis.io/commands/echo
1059
- *
1060
- * @author Daniele Alessandri <suppakilla@gmail.com>
1061
- */
1062
- class ConnectionEcho extends Command
1063
- {
1064
- /**
1065
- * {@inheritdoc}
1066
- */
1067
- public function getId()
1068
- {
1069
- return 'ECHO';
1070
- }
1071
- }
1072
-
1073
- /**
1074
- * @link http://redis.io/commands/zadd
1075
- *
1076
- * @author Daniele Alessandri <suppakilla@gmail.com>
1077
- */
1078
- class ZSetAdd extends Command
1079
- {
1080
- /**
1081
- * {@inheritdoc}
1082
- */
1083
- public function getId()
1084
- {
1085
- return 'ZADD';
1086
- }
1087
-
1088
- /**
1089
- * {@inheritdoc}
1090
- */
1091
- protected function filterArguments(array $arguments)
1092
- {
1093
- if (is_array(end($arguments))) {
1094
- foreach (array_pop($arguments) as $member => $score) {
1095
- $arguments[] = $score;
1096
- $arguments[] = $member;
1097
- }
1098
- }
1099
-
1100
- return $arguments;
1101
- }
1102
- }
1103
-
1104
- /**
1105
- * @link http://redis.io/commands/rpop
1106
- *
1107
- * @author Daniele Alessandri <suppakilla@gmail.com>
1108
- */
1109
- class ListPopLast extends Command
1110
- {
1111
- /**
1112
- * {@inheritdoc}
1113
- */
1114
- public function getId()
1115
- {
1116
- return 'RPOP';
1117
- }
1118
- }
1119
-
1120
- /**
1121
- * @link http://redis.io/commands/del
1122
- *
1123
- * @author Daniele Alessandri <suppakilla@gmail.com>
1124
- */
1125
- class KeyDelete extends Command
1126
- {
1127
- /**
1128
- * {@inheritdoc}
1129
- */
1130
- public function getId()
1131
- {
1132
- return 'DEL';
1133
- }
1134
-
1135
- /**
1136
- * {@inheritdoc}
1137
- */
1138
- protected function filterArguments(array $arguments)
1139
- {
1140
- return self::normalizeArguments($arguments);
1141
- }
1142
- }
1143
-
1144
- /**
1145
- * @link http://redis.io/commands/renamenx
1146
- *
1147
- * @author Daniele Alessandri <suppakilla@gmail.com>
1148
- */
1149
- class KeyRenamePreserve extends KeyRename
1150
- {
1151
- /**
1152
- * {@inheritdoc}
1153
- */
1154
- public function getId()
1155
- {
1156
- return 'RENAMENX';
1157
- }
1158
- }
1159
-
1160
- /**
1161
- * @link http://redis.io/commands/flushall
1162
- *
1163
- * @author Daniele Alessandri <suppakilla@gmail.com>
1164
- */
1165
- class ServerFlushAll extends Command
1166
- {
1167
- /**
1168
- * {@inheritdoc}
1169
- */
1170
- public function getId()
1171
- {
1172
- return 'FLUSHALL';
1173
- }
1174
- }
1175
-
1176
- /**
1177
- * @link http://redis.io/commands/randomkey
1178
- *
1179
- * @author Daniele Alessandri <suppakilla@gmail.com>
1180
- */
1181
- class KeyRandom extends Command
1182
- {
1183
- /**
1184
- * {@inheritdoc}
1185
- */
1186
- public function getId()
1187
- {
1188
- return 'RANDOMKEY';
1189
- }
1190
-
1191
- /**
1192
- * {@inheritdoc}
1193
- */
1194
- public function parseResponse($data)
1195
- {
1196
- return $data !== '' ? $data : null;
1197
- }
1198
- }
1199
-
1200
- /**
1201
- * @link http://redis.io/commands/incrby
1202
- *
1203
- * @author Daniele Alessandri <suppakilla@gmail.com>
1204
- */
1205
- class StringIncrementBy extends Command
1206
- {
1207
- /**
1208
- * {@inheritdoc}
1209
- */
1210
- public function getId()
1211
- {
1212
- return 'INCRBY';
1213
- }
1214
- }
1215
-
1216
- /**
1217
- * @link http://redis.io/commands/hget
1218
- *
1219
- * @author Daniele Alessandri <suppakilla@gmail.com>
1220
- */
1221
- class HashGet extends Command
1222
- {
1223
- /**
1224
- * {@inheritdoc}
1225
- */
1226
- public function getId()
1227
- {
1228
- return 'HGET';
1229
- }
1230
- }
1231
-
1232
- /**
1233
- * @link http://redis.io/commands/zlexcount
1234
- *
1235
- * @author Daniele Alessandri <suppakilla@gmail.com>
1236
- */
1237
- class ZSetLexCount extends Command
1238
- {
1239
- /**
1240
- * {@inheritdoc}
1241
- */
1242
- public function getId()
1243
- {
1244
- return 'ZLEXCOUNT';
1245
- }
1246
- }
1247
-
1248
- /**
1249
- * @link http://redis.io/commands/migrate
1250
- *
1251
- * @author Daniele Alessandri <suppakilla@gmail.com>
1252
- */
1253
- class KeyMigrate extends Command
1254
- {
1255
- /**
1256
- * {@inheritdoc}
1257
- */
1258
- public function getId()
1259
- {
1260
- return 'MIGRATE';
1261
- }
1262
-
1263
- /**
1264
- * {@inheritdoc}
1265
- */
1266
- protected function filterArguments(array $arguments)
1267
- {
1268
- if (is_array(end($arguments))) {
1269
- foreach (array_pop($arguments) as $modifier => $value) {
1270
- $modifier = strtoupper($modifier);
1271
-
1272
- if ($modifier === 'COPY' && $value == true) {
1273
- $arguments[] = $modifier;
1274
- }
1275
-
1276
- if ($modifier === 'REPLACE' && $value == true) {
1277
- $arguments[] = $modifier;
1278
- }
1279
- }
1280
- }
1281
-
1282
- return $arguments;
1283
- }
1284
- }
1285
-
1286
- /**
1287
- * @link http://redis.io/commands/select
1288
- *
1289
- * @author Daniele Alessandri <suppakilla@gmail.com>
1290
- */
1291
- class ConnectionSelect extends Command
1292
- {
1293
- /**
1294
- * {@inheritdoc}
1295
- */
1296
- public function getId()
1297
- {
1298
- return 'SELECT';
1299
- }
1300
- }
1301
-
1302
- /**
1303
- * @link http://redis.io/commands/smembers
1304
- *
1305
- * @author Daniele Alessandri <suppakilla@gmail.com>
1306
- */
1307
- class SetMembers extends Command
1308
- {
1309
- /**
1310
- * {@inheritdoc}
1311
- */
1312
- public function getId()
1313
- {
1314
- return 'SMEMBERS';
1315
- }
1316
- }
1317
-
1318
- /**
1319
- * @link http://redis.io/commands/bitcount
1320
- *
1321
- * @author Daniele Alessandri <suppakilla@gmail.com>
1322
- */
1323
- class StringBitCount extends Command
1324
- {
1325
- /**
1326
- * {@inheritdoc}
1327
- */
1328
- public function getId()
1329
- {
1330
- return 'BITCOUNT';
1331
- }
1332
- }
1333
-
1334
- /**
1335
- * @link http://redis.io/commands/hincrbyfloat
1336
- *
1337
- * @author Daniele Alessandri <suppakilla@gmail.com>
1338
- */
1339
- class HashIncrementByFloat extends Command
1340
- {
1341
- /**
1342
- * {@inheritdoc}
1343
- */
1344
- public function getId()
1345
- {
1346
- return 'HINCRBYFLOAT';
1347
- }
1348
- }
1349
-
1350
- /**
1351
- * @link http://redis.io/commands/getrange
1352
- *
1353
- * @author Daniele Alessandri <suppakilla@gmail.com>
1354
- */
1355
- class StringGetRange extends Command
1356
- {
1357
- /**
1358
- * {@inheritdoc}
1359
- */
1360
- public function getId()
1361
- {
1362
- return 'GETRANGE';
1363
- }
1364
- }
1365
-
1366
- /**
1367
- * @link http://redis.io/commands/brpop
1368
- *
1369
- * @author Daniele Alessandri <suppakilla@gmail.com>
1370
- */
1371
- class ListPopLastBlocking extends ListPopFirstBlocking
1372
- {
1373
- /**
1374
- * {@inheritdoc}
1375
- */
1376
- public function getId()
1377
- {
1378
- return 'BRPOP';
1379
- }
1380
- }
1381
-
1382
- /**
1383
- * @link http://redis.io/commands/getbit
1384
- *
1385
- * @author Daniele Alessandri <suppakilla@gmail.com>
1386
- */
1387
- class StringGetBit extends Command
1388
- {
1389
- /**
1390
- * {@inheritdoc}
1391
- */
1392
- public function getId()
1393
- {
1394
- return 'GETBIT';
1395
- }
1396
- }
1397
-
1398
- /**
1399
- * @link http://redis.io/commands/lastsave
1400
- *
1401
- * @author Daniele Alessandri <suppakilla@gmail.com>
1402
- */
1403
- class ServerLastSave extends Command
1404
- {
1405
- /**
1406
- * {@inheritdoc}
1407
- */
1408
- public function getId()
1409
- {
1410
- return 'LASTSAVE';
1411
- }
1412
- }
1413
-
1414
- /**
1415
- * @link http://redis.io/commands/setbit
1416
- *
1417
- * @author Daniele Alessandri <suppakilla@gmail.com>
1418
- */
1419
- class StringSetBit extends Command
1420
- {
1421
- /**
1422
- * {@inheritdoc}
1423
- */
1424
- public function getId()
1425
- {
1426
- return 'SETBIT';
1427
- }
1428
- }
1429
-
1430
- /**
1431
- * @link http://redis.io/commands/bgrewriteaof
1432
- *
1433
- * @author Daniele Alessandri <suppakilla@gmail.com>
1434
- */
1435
- class ServerBackgroundRewriteAOF extends Command
1436
- {
1437
- /**
1438
- * {@inheritdoc}
1439
- */
1440
- public function getId()
1441
- {
1442
- return 'BGREWRITEAOF';
1443
- }
1444
-
1445
- /**
1446
- * {@inheritdoc}
1447
- */
1448
- public function parseResponse($data)
1449
- {
1450
- return $data == 'Background append only file rewriting started';
1451
- }
1452
- }
1453
-
1454
- /**
1455
- * @link http://redis.io/commands/set
1456
- *
1457
- * @author Daniele Alessandri <suppakilla@gmail.com>
1458
- */
1459
- class StringSet extends Command
1460
- {
1461
- /**
1462
- * {@inheritdoc}
1463
- */
1464
- public function getId()
1465
- {
1466
- return 'SET';
1467
- }
1468
- }
1469
-
1470
- /**
1471
- * @link http://redis.io/commands/info
1472
- *
1473
- * @author Daniele Alessandri <suppakilla@gmail.com>
1474
- */
1475
- class ServerInfoV26x extends ServerInfo
1476
- {
1477
- /**
1478
- * {@inheritdoc}
1479
- */
1480
- public function parseResponse($data)
1481
- {
1482
- if ($data === '') {
1483
- return array();
1484
- }
1485
-
1486
- $info = array();
1487
-
1488
- $current = null;
1489
- $infoLines = preg_split('/\r?\n/', $data);
1490
-
1491
- if (isset($infoLines[0]) && $infoLines[0][0] !== '#') {
1492
- return parent::parseResponse($data);
1493
- }
1494
-
1495
- foreach ($infoLines as $row) {
1496
- if ($row === '') {
1497
- continue;
1498
- }
1499
-
1500
- if (preg_match('/^# (\w+)$/', $row, $matches)) {
1501
- $info[$matches[1]] = array();
1502
- $current = &$info[$matches[1]];
1503
- continue;
1504
- }
1505
-
1506
- list($k, $v) = $this->parseRow($row);
1507
- $current[$k] = $v;
1508
- }
1509
-
1510
- return $info;
1511
- }
1512
- }
1513
-
1514
- /**
1515
- * @link http://redis.io/commands/lrem
1516
- *
1517
- * @author Daniele Alessandri <suppakilla@gmail.com>
1518
- */
1519
- class ListRemove extends Command
1520
- {
1521
- /**
1522
- * {@inheritdoc}
1523
- */
1524
- public function getId()
1525
- {
1526
- return 'LREM';
1527
- }
1528
- }
1529
-
1530
- /**
1531
- * @link http://redis.io/commands/pfmerge
1532
- *
1533
- * @author Daniele Alessandri <suppakilla@gmail.com>
1534
- */
1535
- class HyperLogLogMerge extends Command
1536
- {
1537
- /**
1538
- * {@inheritdoc}
1539
- */
1540
- public function getId()
1541
- {
1542
- return 'PFMERGE';
1543
- }
1544
-
1545
- /**
1546
- * {@inheritdoc}
1547
- */
1548
- protected function filterArguments(array $arguments)
1549
- {
1550
- return self::normalizeArguments($arguments);
1551
- }
1552
- }
1553
-
1554
- /**
1555
- * @link http://redis.io/commands/quit
1556
- *
1557
- * @author Daniele Alessandri <suppakilla@gmail.com>
1558
- */
1559
- class ConnectionQuit extends Command
1560
- {
1561
- /**
1562
- * {@inheritdoc}
1563
- */
1564
- public function getId()
1565
- {
1566
- return 'QUIT';
1567
- }
1568
- }
1569
-
1570
- /**
1571
- * @link http://redis.io/commands/bitop
1572
- *
1573
- * @author Daniele Alessandri <suppakilla@gmail.com>
1574
- */
1575
- class StringBitOp extends Command
1576
- {
1577
- /**
1578
- * {@inheritdoc}
1579
- */
1580
- public function getId()
1581
- {
1582
- return 'BITOP';
1583
- }
1584
-
1585
- /**
1586
- * {@inheritdoc}
1587
- */
1588
- protected function filterArguments(array $arguments)
1589
- {
1590
- if (count($arguments) === 3 && is_array($arguments[2])) {
1591
- list($operation, $destination) = $arguments;
1592
- $arguments = $arguments[2];
1593
- array_unshift($arguments, $operation, $destination);
1594
- }
1595
-
1596
- return $arguments;
1597
- }
1598
- }
1599
-
1600
- /**
1601
- * @link http://redis.io/commands/lpop
1602
- *
1603
- * @author Daniele Alessandri <suppakilla@gmail.com>
1604
- */
1605
- class ListPopFirst extends Command
1606
- {
1607
- /**
1608
- * {@inheritdoc}
1609
- */
1610
- public function getId()
1611
- {
1612
- return 'LPOP';
1613
- }
1614
- }
1615
-
1616
- /**
1617
- * @link http://redis.io/commands/watch
1618
- *
1619
- * @author Daniele Alessandri <suppakilla@gmail.com>
1620
- */
1621
- class TransactionWatch extends Command
1622
- {
1623
- /**
1624
- * {@inheritdoc}
1625
- */
1626
- public function getId()
1627
- {
1628
- return 'WATCH';
1629
- }
1630
-
1631
- /**
1632
- * {@inheritdoc}
1633
- */
1634
- protected function filterArguments(array $arguments)
1635
- {
1636
- if (isset($arguments[0]) && is_array($arguments[0])) {
1637
- return $arguments[0];
1638
- }
1639
-
1640
- return $arguments;
1641
- }
1642
- }
1643
-
1644
- /**
1645
- * @link http://redis.io/commands/auth
1646
- *
1647
- * @author Daniele Alessandri <suppakilla@gmail.com>
1648
- */
1649
- class ConnectionAuth extends Command
1650
- {
1651
- /**
1652
- * {@inheritdoc}
1653
- */
1654
- public function getId()
1655
- {
1656
- return 'AUTH';
1657
- }
1658
- }
1659
-
1660
- /**
1661
- * @link http://redis.io/commands/sismember
1662
- *
1663
- * @author Daniele Alessandri <suppakilla@gmail.com>
1664
- */
1665
- class SetIsMember extends Command
1666
- {
1667
- /**
1668
- * {@inheritdoc}
1669
- */
1670
- public function getId()
1671
- {
1672
- return 'SISMEMBER';
1673
- }
1674
- }
1675
-
1676
- /**
1677
- * @link http://redis.io/commands/hstrlen
1678
- *
1679
- * @author Daniele Alessandri <suppakilla@gmail.com>
1680
- */
1681
- class HashStringLength extends Command
1682
- {
1683
- /**
1684
- * {@inheritdoc}
1685
- */
1686
- public function getId()
1687
- {
1688
- return 'HSTRLEN';
1689
- }
1690
- }
1691
-
1692
- /**
1693
- * @link http://redis.io/commands/monitor
1694
- *
1695
- * @author Daniele Alessandri <suppakilla@gmail.com>
1696
- */
1697
- class ServerMonitor extends Command
1698
- {
1699
- /**
1700
- * {@inheritdoc}
1701
- */
1702
- public function getId()
1703
- {
1704
- return 'MONITOR';
1705
- }
1706
- }
1707
-
1708
- /**
1709
- * @link http://redis.io/commands/pfcount
1710
- *
1711
- * @author Daniele Alessandri <suppakilla@gmail.com>
1712
- */
1713
- class HyperLogLogCount extends Command
1714
- {
1715
- /**
1716
- * {@inheritdoc}
1717
- */
1718
- public function getId()
1719
- {
1720
- return 'PFCOUNT';
1721
- }
1722
-
1723
- /**
1724
- * {@inheritdoc}
1725
- */
1726
- protected function filterArguments(array $arguments)
1727
- {
1728
- return self::normalizeArguments($arguments);
1729
- }
1730
- }
1731
-
1732
- /**
1733
- * @link http://redis.io/commands/georadiusbymember
1734
- *
1735
- * @author Daniele Alessandri <suppakilla@gmail.com>
1736
- */
1737
- class GeospatialGeoRadiusByMember extends GeospatialGeoRadius
1738
- {
1739
- /**
1740
- * {@inheritdoc}
1741
- */
1742
- public function getId()
1743
- {
1744
- return 'GEORADIUSBYMEMBER';
1745
- }
1746
- }
1747
-
1748
- /**
1749
- * @link http://redis.io/commands/lpushx
1750
- *
1751
- * @author Daniele Alessandri <suppakilla@gmail.com>
1752
- */
1753
- class ListPushHeadX extends Command
1754
- {
1755
- /**
1756
- * {@inheritdoc}
1757
- */
1758
- public function getId()
1759
- {
1760
- return 'LPUSHX';
1761
- }
1762
- }
1763
-
1764
- /**
1765
- * @link http://redis.io/commands/zcard
1766
- *
1767
- * @author Daniele Alessandri <suppakilla@gmail.com>
1768
- */
1769
- class ZSetCardinality extends Command
1770
- {
1771
- /**
1772
- * {@inheritdoc}
1773
- */
1774
- public function getId()
1775
- {
1776
- return 'ZCARD';
1777
- }
1778
- }
1779
-
1780
- /**
1781
- * @link http://redis.io/commands/hmget
1782
- *
1783
- * @author Daniele Alessandri <suppakilla@gmail.com>
1784
- */
1785
- class HashGetMultiple extends Command
1786
- {
1787
- /**
1788
- * {@inheritdoc}
1789
- */
1790
- public function getId()
1791
- {
1792
- return 'HMGET';
1793
- }
1794
-
1795
- /**
1796
- * {@inheritdoc}
1797
- */
1798
- protected function filterArguments(array $arguments)
1799
- {
1800
- return self::normalizeVariadic($arguments);
1801
- }
1802
- }
1803
-
1804
- /**
1805
- * @link http://redis.io/commands/sunion
1806
- *
1807
- * @author Daniele Alessandri <suppakilla@gmail.com>
1808
- */
1809
- class SetUnion extends SetIntersection
1810
- {
1811
- /**
1812
- * {@inheritdoc}
1813
- */
1814
- public function getId()
1815
- {
1816
- return 'SUNION';
1817
- }
1818
- }
1819
-
1820
- /**
1821
- * @link http://redis.io/commands/zrevrank
1822
- *
1823
- * @author Daniele Alessandri <suppakilla@gmail.com>
1824
- */
1825
- class ZSetReverseRank extends Command
1826
- {
1827
- /**
1828
- * {@inheritdoc}
1829
- */
1830
- public function getId()
1831
- {
1832
- return 'ZREVRANK';
1833
- }
1834
- }
1835
-
1836
- /**
1837
- * @link http://redis.io/commands/slaveof
1838
- *
1839
- * @author Daniele Alessandri <suppakilla@gmail.com>
1840
- */
1841
- class ServerSlaveOf extends Command
1842
- {
1843
- /**
1844
- * {@inheritdoc}
1845
- */
1846
- public function getId()
1847
- {
1848
- return 'SLAVEOF';
1849
- }
1850
-
1851
- /**
1852
- * {@inheritdoc}
1853
- */
1854
- protected function filterArguments(array $arguments)
1855
- {
1856
- if (count($arguments) === 0 || $arguments[0] === 'NO ONE') {
1857
- return array('NO', 'ONE');
1858
- }
1859
-
1860
- return $arguments;
1861
- }
1862
- }
1863
-
1864
- /**
1865
- * @link http://redis.io/commands/incrbyfloat
1866
- *
1867
- * @author Daniele Alessandri <suppakilla@gmail.com>
1868
- */
1869
- class StringIncrementByFloat extends Command
1870
- {
1871
- /**
1872
- * {@inheritdoc}
1873
- */
1874
- public function getId()
1875
- {
1876
- return 'INCRBYFLOAT';
1877
- }
1878
- }
1879
-
1880
- /**
1881
- * @link http://redis.io/commands/type
1882
- *
1883
- * @author Daniele Alessandri <suppakilla@gmail.com>
1884
- */
1885
- class KeyType extends Command
1886
- {
1887
- /**
1888
- * {@inheritdoc}
1889
- */
1890
- public function getId()
1891
- {
1892
- return 'TYPE';
1893
- }
1894
- }
1895
-
1896
- /**
1897
- * @link http://redis.io/commands/hvals
1898
- *
1899
- * @author Daniele Alessandri <suppakilla@gmail.com>
1900
- */
1901
- class HashValues extends Command
1902
- {
1903
- /**
1904
- * {@inheritdoc}
1905
- */
1906
- public function getId()
1907
- {
1908
- return 'HVALS';
1909
- }
1910
- }
1911
-
1912
- /**
1913
- * @link http://redis.io/commands/unwatch
1914
- *
1915
- * @author Daniele Alessandri <suppakilla@gmail.com>
1916
- */
1917
- class TransactionUnwatch extends Command
1918
- {
1919
- /**
1920
- * {@inheritdoc}
1921
- */
1922
- public function getId()
1923
- {
1924
- return 'UNWATCH';
1925
- }
1926
- }
1927
-
1928
- /**
1929
- * @link http://redis.io/commands/sunionstore
1930
- *
1931
- * @author Daniele Alessandri <suppakilla@gmail.com>
1932
- */
1933
- class SetUnionStore extends SetIntersectionStore
1934
- {
1935
- /**
1936
- * {@inheritdoc}
1937
- */
1938
- public function getId()
1939
- {
1940
- return 'SUNIONSTORE';
1941
- }
1942
- }
1943
-
1944
- /**
1945
- * @link http://redis.io/commands/setrange
1946
- *
1947
- * @author Daniele Alessandri <suppakilla@gmail.com>
1948
- */
1949
- class StringSetRange extends Command
1950
- {
1951
- /**
1952
- * {@inheritdoc}
1953
- */
1954
- public function getId()
1955
- {
1956
- return 'SETRANGE';
1957
- }
1958
- }
1959
-
1960
- /**
1961
- * @link http://redis.io/commands/geopos
1962
- *
1963
- * @author Daniele Alessandri <suppakilla@gmail.com>
1964
- */
1965
- class GeospatialGeoPos extends Command
1966
- {
1967
- /**
1968
- * {@inheritdoc}
1969
- */
1970
- public function getId()
1971
- {
1972
- return 'GEOPOS';
1973
- }
1974
-
1975
- /**
1976
- * {@inheritdoc}
1977
- */
1978
- protected function filterArguments(array $arguments)
1979
- {
1980
- if (count($arguments) === 2 && is_array($arguments[1])) {
1981
- $members = array_pop($arguments);
1982
- $arguments = array_merge($arguments, $members);
1983
- }
1984
-
1985
- return $arguments;
1986
- }
1987
- }
1988
-
1989
- /**
1990
- * @link http://redis.io/commands/rpushx
1991
- *
1992
- * @author Daniele Alessandri <suppakilla@gmail.com>
1993
- */
1994
- class ListPushTailX extends Command
1995
- {
1996
- /**
1997
- * {@inheritdoc}
1998
- */
1999
- public function getId()
2000
- {
2001
- return 'RPUSHX';
2002
- }
2003
- }
2004
-
2005
- /**
2006
- * @link http://redis.io/commands/dump
2007
- *
2008
- * @author Daniele Alessandri <suppakilla@gmail.com>
2009
- */
2010
- class KeyDump extends Command
2011
- {
2012
- /**
2013
- * {@inheritdoc}
2014
- */
2015
- public function getId()
2016
- {
2017
- return 'DUMP';
2018
- }
2019
- }
2020
-
2021
- /**
2022
- * @link http://redis.io/commands/keys
2023
- *
2024
- * @author Daniele Alessandri <suppakilla@gmail.com>
2025
- */
2026
- class KeyKeys extends Command
2027
- {
2028
- /**
2029
- * {@inheritdoc}
2030
- */
2031
- public function getId()
2032
- {
2033
- return 'KEYS';
2034
- }
2035
- }
2036
-
2037
- /**
2038
- * @link http://redis.io/commands/setnx
2039
- *
2040
- * @author Daniele Alessandri <suppakilla@gmail.com>
2041
- */
2042
- class StringSetPreserve extends Command
2043
- {
2044
- /**
2045
- * {@inheritdoc}
2046
- */
2047
- public function getId()
2048
- {
2049
- return 'SETNX';
2050
- }
2051
- }
2052
-
2053
- /**
2054
- * @link http://redis.io/commands/config-set
2055
- * @link http://redis.io/commands/config-get
2056
- * @link http://redis.io/commands/config-resetstat
2057
- * @link http://redis.io/commands/config-rewrite
2058
- *
2059
- * @author Daniele Alessandri <suppakilla@gmail.com>
2060
- */
2061
- class ServerConfig extends Command
2062
- {
2063
- /**
2064
- * {@inheritdoc}
2065
- */
2066
- public function getId()
2067
- {
2068
- return 'CONFIG';
2069
- }
2070
-
2071
- /**
2072
- * {@inheritdoc}
2073
- */
2074
- public function parseResponse($data)
2075
- {
2076
- if (is_array($data)) {
2077
- $result = array();
2078
-
2079
- for ($i = 0; $i < count($data); ++$i) {
2080
- $result[$data[$i]] = $data[++$i];
2081
- }
2082
-
2083
- return $result;
2084
- }
2085
-
2086
- return $data;
2087
- }
2088
- }
2089
-
2090
- /**
2091
- * @link http://redis.io/commands/punsubscribe
2092
- *
2093
- * @author Daniele Alessandri <suppakilla@gmail.com>
2094
- */
2095
- class PubSubUnsubscribeByPattern extends PubSubUnsubscribe
2096
- {
2097
- /**
2098
- * {@inheritdoc}
2099
- */
2100
- public function getId()
2101
- {
2102
- return 'PUNSUBSCRIBE';
2103
- }
2104
- }
2105
-
2106
- /**
2107
- * @link http://redis.io/commands/zrank
2108
- *
2109
- * @author Daniele Alessandri <suppakilla@gmail.com>
2110
- */
2111
- class ZSetRank extends Command
2112
- {
2113
- /**
2114
- * {@inheritdoc}
2115
- */
2116
- public function getId()
2117
- {
2118
- return 'ZRANK';
2119
- }
2120
- }
2121
-
2122
- /**
2123
- * @link http://redis.io/commands/hscan
2124
- *
2125
- * @author Daniele Alessandri <suppakilla@gmail.com>
2126
- */
2127
- class HashScan extends Command
2128
- {
2129
- /**
2130
- * {@inheritdoc}
2131
- */
2132
- public function getId()
2133
- {
2134
- return 'HSCAN';
2135
- }
2136
-
2137
- /**
2138
- * {@inheritdoc}
2139
- */
2140
- protected function filterArguments(array $arguments)
2141
- {
2142
- if (count($arguments) === 3 && is_array($arguments[2])) {
2143
- $options = $this->prepareOptions(array_pop($arguments));
2144
- $arguments = array_merge($arguments, $options);
2145
- }
2146
-
2147
- return $arguments;
2148
- }
2149
-
2150
- /**
2151
- * Returns a list of options and modifiers compatible with Redis.
2152
- *
2153
- * @param array $options List of options.
2154
- *
2155
- * @return array
2156
- */
2157
- protected function prepareOptions($options)
2158
- {
2159
- $options = array_change_key_case($options, CASE_UPPER);
2160
- $normalized = array();
2161
-
2162
- if (!empty($options['MATCH'])) {
2163
- $normalized[] = 'MATCH';
2164
- $normalized[] = $options['MATCH'];
2165
- }
2166
-
2167
- if (!empty($options['COUNT'])) {
2168
- $normalized[] = 'COUNT';
2169
- $normalized[] = $options['COUNT'];
2170
- }
2171
-
2172
- return $normalized;
2173
- }
2174
-
2175
- /**
2176
- * {@inheritdoc}
2177
- */
2178
- public function parseResponse($data)
2179
- {
2180
- if (is_array($data)) {
2181
- $fields = $data[1];
2182
- $result = array();
2183
-
2184
- for ($i = 0; $i < count($fields); ++$i) {
2185
- $result[$fields[$i]] = $fields[++$i];
2186
- }
2187
-
2188
- $data[1] = $result;
2189
- }
2190
-
2191
- return $data;
2192
- }
2193
- }
2194
-
2195
- /**
2196
- * @link http://redis.io/commands/multi
2197
- *
2198
- * @author Daniele Alessandri <suppakilla@gmail.com>
2199
- */
2200
- class TransactionMulti extends Command
2201
- {
2202
- /**
2203
- * {@inheritdoc}
2204
- */
2205
- public function getId()
2206
- {
2207
- return 'MULTI';
2208
- }
2209
- }
2210
-
2211
- /**
2212
- * @link http://redis.io/commands/sscan
2213
- *
2214
- * @author Daniele Alessandri <suppakilla@gmail.com>
2215
- */
2216
- class SetScan extends Command
2217
- {
2218
- /**
2219
- * {@inheritdoc}
2220
- */
2221
- public function getId()
2222
- {
2223
- return 'SSCAN';
2224
- }
2225
-
2226
- /**
2227
- * {@inheritdoc}
2228
- */
2229
- protected function filterArguments(array $arguments)
2230
- {
2231
- if (count($arguments) === 3 && is_array($arguments[2])) {
2232
- $options = $this->prepareOptions(array_pop($arguments));
2233
- $arguments = array_merge($arguments, $options);
2234
- }
2235
-
2236
- return $arguments;
2237
- }
2238
-
2239
- /**
2240
- * Returns a list of options and modifiers compatible with Redis.
2241
- *
2242
- * @param array $options List of options.
2243
- *
2244
- * @return array
2245
- */
2246
- protected function prepareOptions($options)
2247
- {
2248
- $options = array_change_key_case($options, CASE_UPPER);
2249
- $normalized = array();
2250
-
2251
- if (!empty($options['MATCH'])) {
2252
- $normalized[] = 'MATCH';
2253
- $normalized[] = $options['MATCH'];
2254
- }
2255
-
2256
- if (!empty($options['COUNT'])) {
2257
- $normalized[] = 'COUNT';
2258
- $normalized[] = $options['COUNT'];
2259
- }
2260
-
2261
- return $normalized;
2262
- }
2263
- }
2264
-
2265
- /**
2266
- * @link http://redis.io/commands/command
2267
- *
2268
- * @author Daniele Alessandri <suppakilla@gmail.com>
2269
- */
2270
- class ServerCommand extends Command
2271
- {
2272
- /**
2273
- * {@inheritdoc}
2274
- */
2275
- public function getId()
2276
- {
2277
- return 'COMMAND';
2278
- }
2279
- }
2280
-
2281
- /**
2282
- * @link http://redis.io/commands/zscan
2283
- *
2284
- * @author Daniele Alessandri <suppakilla@gmail.com>
2285
- */
2286
- class ZSetScan extends Command
2287
- {
2288
- /**
2289
- * {@inheritdoc}
2290
- */
2291
- public function getId()
2292
- {
2293
- return 'ZSCAN';
2294
- }
2295
-
2296
- /**
2297
- * {@inheritdoc}
2298
- */
2299
- protected function filterArguments(array $arguments)
2300
- {
2301
- if (count($arguments) === 3 && is_array($arguments[2])) {
2302
- $options = $this->prepareOptions(array_pop($arguments));
2303
- $arguments = array_merge($arguments, $options);
2304
- }
2305
-
2306
- return $arguments;
2307
- }
2308
-
2309
- /**
2310
- * Returns a list of options and modifiers compatible with Redis.
2311
- *
2312
- * @param array $options List of options.
2313
- *
2314
- * @return array
2315
- */
2316
- protected function prepareOptions($options)
2317
- {
2318
- $options = array_change_key_case($options, CASE_UPPER);
2319
- $normalized = array();
2320
-
2321
- if (!empty($options['MATCH'])) {
2322
- $normalized[] = 'MATCH';
2323
- $normalized[] = $options['MATCH'];
2324
- }
2325
-
2326
- if (!empty($options['COUNT'])) {
2327
- $normalized[] = 'COUNT';
2328
- $normalized[] = $options['COUNT'];
2329
- }
2330
-
2331
- return $normalized;
2332
- }
2333
-
2334
- /**
2335
- * {@inheritdoc}
2336
- */
2337
- public function parseResponse($data)
2338
- {
2339
- if (is_array($data)) {
2340
- $members = $data[1];
2341
- $result = array();
2342
-
2343
- for ($i = 0; $i < count($members); ++$i) {
2344
- $result[$members[$i]] = (float) $members[++$i];
2345
- }
2346
-
2347
- $data[1] = $result;
2348
- }
2349
-
2350
- return $data;
2351
- }
2352
- }
2353
-
2354
- /**
2355
- * Class for generic "anonymous" Redis commands.
2356
- *
2357
- * This command class does not filter input arguments or parse responses, but
2358
- * can be used to leverage the standard Predis API to execute any command simply
2359
- * by providing the needed arguments following the command signature as defined
2360
- * by Redis in its documentation.
2361
- *
2362
- * @author Daniele Alessandri <suppakilla@gmail.com>
2363
- */
2364
- class RawCommand implements CommandInterface
2365
- {
2366
- private $slot;
2367
- private $commandID;
2368
- private $arguments;
2369
-
2370
- /**
2371
- * @param array $arguments Command ID and its arguments.
2372
- *
2373
- * @throws \InvalidArgumentException
2374
- */
2375
- public function __construct(array $arguments)
2376
- {
2377
- if (!$arguments) {
2378
- throw new \InvalidArgumentException(
2379
- 'The arguments array must contain at least the command ID.'
2380
- );
2381
- }
2382
-
2383
- $this->commandID = strtoupper(array_shift($arguments));
2384
- $this->arguments = $arguments;
2385
- }
2386
-
2387
- /**
2388
- * Creates a new raw command using a variadic method.
2389
- *
2390
- * @param string $commandID Redis command ID.
2391
- * @param string ... Arguments list for the command.
2392
- *
2393
- * @return CommandInterface
2394
- */
2395
- public static function create($commandID /* [ $arg, ... */)
2396
- {
2397
- $arguments = func_get_args();
2398
- $command = new self($arguments);
2399
-
2400
- return $command;
2401
- }
2402
-
2403
- /**
2404
- * {@inheritdoc}
2405
- */
2406
- public function getId()
2407
- {
2408
- return $this->commandID;
2409
- }
2410
-
2411
- /**
2412
- * {@inheritdoc}
2413
- */
2414
- public function setArguments(array $arguments)
2415
- {
2416
- $this->arguments = $arguments;
2417
- unset($this->slot);
2418
- }
2419
-
2420
- /**
2421
- * {@inheritdoc}
2422
- */
2423
- public function setRawArguments(array $arguments)
2424
- {
2425
- $this->setArguments($arguments);
2426
- }
2427
-
2428
- /**
2429
- * {@inheritdoc}
2430
- */
2431
- public function getArguments()
2432
- {
2433
- return $this->arguments;
2434
- }
2435
-
2436
- /**
2437
- * {@inheritdoc}
2438
- */
2439
- public function getArgument($index)
2440
- {
2441
- if (isset($this->arguments[$index])) {
2442
- return $this->arguments[$index];
2443
- }
2444
- }
2445
-
2446
- /**
2447
- * {@inheritdoc}
2448
- */
2449
- public function setSlot($slot)
2450
- {
2451
- $this->slot = $slot;
2452
- }
2453
-
2454
- /**
2455
- * {@inheritdoc}
2456
- */
2457
- public function getSlot()
2458
- {
2459
- if (isset($this->slot)) {
2460
- return $this->slot;
2461
- }
2462
- }
2463
-
2464
- /**
2465
- * {@inheritdoc}
2466
- */
2467
- public function parseResponse($data)
2468
- {
2469
- return $data;
2470
- }
2471
- }
2472
-
2473
- /**
2474
- * @link http://redis.io/commands/decrby
2475
- *
2476
- * @author Daniele Alessandri <suppakilla@gmail.com>
2477
- */
2478
- class StringDecrementBy extends Command
2479
- {
2480
- /**
2481
- * {@inheritdoc}
2482
- */
2483
- public function getId()
2484
- {
2485
- return 'DECRBY';
2486
- }
2487
- }
2488
-
2489
- /**
2490
- * @link http://redis.io/commands/substr
2491
- *
2492
- * @author Daniele Alessandri <suppakilla@gmail.com>
2493
- */
2494
- class StringSubstr extends Command
2495
- {
2496
- /**
2497
- * {@inheritdoc}
2498
- */
2499
- public function getId()
2500
- {
2501
- return 'SUBSTR';
2502
- }
2503
- }
2504
-
2505
- /**
2506
- * @link http://redis.io/commands/hdel
2507
- *
2508
- * @author Daniele Alessandri <suppakilla@gmail.com>
2509
- */
2510
- class HashDelete extends Command
2511
- {
2512
- /**
2513
- * {@inheritdoc}
2514
- */
2515
- public function getId()
2516
- {
2517
- return 'HDEL';
2518
- }
2519
-
2520
- /**
2521
- * {@inheritdoc}
2522
- */
2523
- protected function filterArguments(array $arguments)
2524
- {
2525
- return self::normalizeVariadic($arguments);
2526
- }
2527
- }
2528
-
2529
- /**
2530
- * @link http://redis.io/topics/sentinel
2531
- *
2532
- * @author Daniele Alessandri <suppakilla@gmail.com>
2533
- */
2534
- class ServerSentinel extends Command
2535
- {
2536
- /**
2537
- * {@inheritdoc}
2538
- */
2539
- public function getId()
2540
- {
2541
- return 'SENTINEL';
2542
- }
2543
-
2544
- /**
2545
- * {@inheritdoc}
2546
- */
2547
- public function parseResponse($data)
2548
- {
2549
- switch (strtolower($this->getArgument(0))) {
2550
- case 'masters':
2551
- case 'slaves':
2552
- return self::processMastersOrSlaves($data);
2553
-
2554
- default:
2555
- return $data;
2556
- }
2557
- }
2558
-
2559
- /**
2560
- * Returns a processed response to SENTINEL MASTERS or SENTINEL SLAVES.
2561
- *
2562
- * @param array $servers List of Redis servers.
2563
- *
2564
- * @return array
2565
- */
2566
- protected static function processMastersOrSlaves(array $servers)
2567
- {
2568
- foreach ($servers as $idx => $node) {
2569
- $processed = array();
2570
- $count = count($node);
2571
-
2572
- for ($i = 0; $i < $count; ++$i) {
2573
- $processed[$node[$i]] = $node[++$i];
2574
- }
2575
-
2576
- $servers[$idx] = $processed;
2577
- }
2578
-
2579
- return $servers;
2580
- }
2581
- }
2582
-
2583
- /**
2584
- * @link http://redis.io/commands/flushdb
2585
- *
2586
- * @author Daniele Alessandri <suppakilla@gmail.com>
2587
- */
2588
- class ServerFlushDatabase extends Command
2589
- {
2590
- /**
2591
- * {@inheritdoc}
2592
- */
2593
- public function getId()
2594
- {
2595
- return 'FLUSHDB';
2596
- }
2597
- }
2598
-
2599
- /**
2600
- * @link http://redis.io/commands/pexpireat
2601
- *
2602
- * @author Daniele Alessandri <suppakilla@gmail.com>
2603
- */
2604
- class KeyPreciseExpireAt extends KeyExpireAt
2605
- {
2606
- /**
2607
- * {@inheritdoc}
2608
- */
2609
- public function getId()
2610
- {
2611
- return 'PEXPIREAT';
2612
- }
2613
- }
2614
-
2615
- /**
2616
- * @link http://redis.io/commands/publish
2617
- *
2618
- * @author Daniele Alessandri <suppakilla@gmail.com>
2619
- */
2620
- class PubSubPublish extends Command
2621
- {
2622
- /**
2623
- * {@inheritdoc}
2624
- */
2625
- public function getId()
2626
- {
2627
- return 'PUBLISH';
2628
- }
2629
- }
2630
-
2631
- /**
2632
- * @link http://redis.io/commands/psubscribe
2633
- *
2634
- * @author Daniele Alessandri <suppakilla@gmail.com>
2635
- */
2636
- class PubSubSubscribeByPattern extends PubSubSubscribe
2637
- {
2638
- /**
2639
- * {@inheritdoc}
2640
- */
2641
- public function getId()
2642
- {
2643
- return 'PSUBSCRIBE';
2644
- }
2645
- }
2646
-
2647
- /**
2648
- * @link http://redis.io/commands/script
2649
- *
2650
- * @author Daniele Alessandri <suppakilla@gmail.com>
2651
- */
2652
- class ServerScript extends Command
2653
- {
2654
- /**
2655
- * {@inheritdoc}
2656
- */
2657
- public function getId()
2658
- {
2659
- return 'SCRIPT';
2660
- }
2661
- }
2662
-
2663
- /**
2664
- * @link http://redis.io/commands/ltrim
2665
- *
2666
- * @author Daniele Alessandri <suppakilla@gmail.com>
2667
- */
2668
- class ListTrim extends Command
2669
- {
2670
- /**
2671
- * {@inheritdoc}
2672
- */
2673
- public function getId()
2674
- {
2675
- return 'LTRIM';
2676
- }
2677
- }
2678
-
2679
- /**
2680
- * @link http://redis.io/commands/rpoplpush
2681
- *
2682
- * @author Daniele Alessandri <suppakilla@gmail.com>
2683
- */
2684
- class ListPopLastPushHead extends Command
2685
- {
2686
- /**
2687
- * {@inheritdoc}
2688
- */
2689
- public function getId()
2690
- {
2691
- return 'RPOPLPUSH';
2692
- }
2693
- }
2694
-
2695
- /**
2696
- * @link http://redis.io/commands/zremrangebylex
2697
- *
2698
- * @author Daniele Alessandri <suppakilla@gmail.com>
2699
- */
2700
- class ZSetRemoveRangeByLex extends Command
2701
- {
2702
- /**
2703
- * {@inheritdoc}
2704
- */
2705
- public function getId()
2706
- {
2707
- return 'ZREMRANGEBYLEX';
2708
- }
2709
- }
2710
-
2711
- /**
2712
- * @link http://redis.io/commands/zrevrange
2713
- *
2714
- * @author Daniele Alessandri <suppakilla@gmail.com>
2715
- */
2716
- class ZSetReverseRange extends ZSetRange
2717
- {
2718
- /**
2719
- * {@inheritdoc}
2720
- */
2721
- public function getId()
2722
- {
2723
- return 'ZREVRANGE';
2724
- }
2725
- }
2726
-
2727
- /**
2728
- * @link http://redis.io/commands/srandmember
2729
- *
2730
- * @author Daniele Alessandri <suppakilla@gmail.com>
2731
- */
2732
- class SetRandomMember extends Command
2733
- {
2734
- /**
2735
- * {@inheritdoc}
2736
- */
2737
- public function getId()
2738
- {
2739
- return 'SRANDMEMBER';
2740
- }
2741
- }
2742
-
2743
- /**
2744
- * @link http://redis.io/commands/hsetnx
2745
- *
2746
- * @author Daniele Alessandri <suppakilla@gmail.com>
2747
- */
2748
- class HashSetPreserve extends Command
2749
- {
2750
- /**
2751
- * {@inheritdoc}
2752
- */
2753
- public function getId()
2754
- {
2755
- return 'HSETNX';
2756
- }
2757
- }
2758
-
2759
- /**
2760
- * @link http://redis.io/commands/exec
2761
- *
2762
- * @author Daniele Alessandri <suppakilla@gmail.com>
2763
- */
2764
- class TransactionExec extends Command
2765
- {
2766
- /**
2767
- * {@inheritdoc}
2768
- */
2769
- public function getId()
2770
- {
2771
- return 'EXEC';
2772
- }
2773
- }
2774
-
2775
- /**
2776
- * @link http://redis.io/commands/msetnx
2777
- *
2778
- * @author Daniele Alessandri <suppakilla@gmail.com>
2779
- */
2780
- class StringSetMultiplePreserve extends StringSetMultiple
2781
- {
2782
- /**
2783
- * {@inheritdoc}
2784
- */
2785
- public function getId()
2786
- {
2787
- return 'MSETNX';
2788
- }
2789
- }
2790
-
2791
- /**
2792
- * @link http://redis.io/commands/pexpire
2793
- *
2794
- * @author Daniele Alessandri <suppakilla@gmail.com>
2795
- */
2796
- class KeyPreciseExpire extends KeyExpire
2797
- {
2798
- /**
2799
- * {@inheritdoc}
2800
- */
2801
- public function getId()
2802
- {
2803
- return 'PEXPIRE';
2804
- }
2805
- }
2806
-
2807
- /**
2808
- * @link http://redis.io/commands/dbsize
2809
- *
2810
- * @author Daniele Alessandri <suppakilla@gmail.com>
2811
- */
2812
- class ServerDatabaseSize extends Command
2813
- {
2814
- /**
2815
- * {@inheritdoc}
2816
- */
2817
- public function getId()
2818
- {
2819
- return 'DBSIZE';
2820
- }
2821
- }
2822
-
2823
- /**
2824
- * @link http://redis.io/commands/sort
2825
- *
2826
- * @author Daniele Alessandri <suppakilla@gmail.com>
2827
- */
2828
- class KeySort extends Command
2829
- {
2830
- /**
2831
- * {@inheritdoc}
2832
- */
2833
- public function getId()
2834
- {
2835
- return 'SORT';
2836
- }
2837
-
2838
- /**
2839
- * {@inheritdoc}
2840
- */
2841
- protected function filterArguments(array $arguments)
2842
- {
2843
- if (count($arguments) === 1) {
2844
- return $arguments;
2845
- }
2846
-
2847
- $query = array($arguments[0]);
2848
- $sortParams = array_change_key_case($arguments[1], CASE_UPPER);
2849
-
2850
- if (isset($sortParams['BY'])) {
2851
- $query[] = 'BY';
2852
- $query[] = $sortParams['BY'];
2853
- }
2854
-
2855
- if (isset($sortParams['GET'])) {
2856
- $getargs = $sortParams['GET'];
2857
-
2858
- if (is_array($getargs)) {
2859
- foreach ($getargs as $getarg) {
2860
- $query[] = 'GET';
2861
- $query[] = $getarg;
2862
- }
2863
- } else {
2864
- $query[] = 'GET';
2865
- $query[] = $getargs;
2866
- }
2867
- }
2868
-
2869
- if (isset($sortParams['LIMIT']) &&
2870
- is_array($sortParams['LIMIT']) &&
2871
- count($sortParams['LIMIT']) == 2) {
2872
- $query[] = 'LIMIT';
2873
- $query[] = $sortParams['LIMIT'][0];
2874
- $query[] = $sortParams['LIMIT'][1];
2875
- }
2876
-
2877
- if (isset($sortParams['SORT'])) {
2878
- $query[] = strtoupper($sortParams['SORT']);
2879
- }
2880
-
2881
- if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) {
2882
- $query[] = 'ALPHA';
2883
- }
2884
-
2885
- if (isset($sortParams['STORE'])) {
2886
- $query[] = 'STORE';
2887
- $query[] = $sortParams['STORE'];
2888
- }
2889
-
2890
- return $query;
2891
- }
2892
- }
2893
-
2894
- /**
2895
- * @link http://redis.io/commands/hkeys
2896
- *
2897
- * @author Daniele Alessandri <suppakilla@gmail.com>
2898
- */
2899
- class HashKeys extends Command
2900
- {
2901
- /**
2902
- * {@inheritdoc}
2903
- */
2904
- public function getId()
2905
- {
2906
- return 'HKEYS';
2907
- }
2908
- }
2909
-
2910
- /**
2911
- * @link http://redis.io/commands/hincrby
2912
- *
2913
- * @author Daniele Alessandri <suppakilla@gmail.com>
2914
- */
2915
- class HashIncrementBy extends Command
2916
- {
2917
- /**
2918
- * {@inheritdoc}
2919
- */
2920
- public function getId()
2921
- {
2922
- return 'HINCRBY';
2923
- }
2924
- }
2925
-
2926
- /**
2927
- * @link http://redis.io/commands/zremrangebyscore
2928
- *
2929
- * @author Daniele Alessandri <suppakilla@gmail.com>
2930
- */
2931
- class ZSetRemoveRangeByScore extends Command
2932
- {
2933
- /**
2934
- * {@inheritdoc}
2935
- */
2936
- public function getId()
2937
- {
2938
- return 'ZREMRANGEBYSCORE';
2939
- }
2940
- }
2941
-
2942
- /**
2943
- * @link http://redis.io/commands/scard
2944
- *
2945
- * @author Daniele Alessandri <suppakilla@gmail.com>
2946
- */
2947
- class SetCardinality extends Command
2948
- {
2949
- /**
2950
- * {@inheritdoc}
2951
- */
2952
- public function getId()
2953
- {
2954
- return 'SCARD';
2955
- }
2956
- }
2957
-
2958
- /**
2959
- * @link http://redis.io/commands/hset
2960
- *
2961
- * @author Daniele Alessandri <suppakilla@gmail.com>
2962
- */
2963
- class HashSet extends Command
2964
- {
2965
- /**
2966
- * {@inheritdoc}
2967
- */
2968
- public function getId()
2969
- {
2970
- return 'HSET';
2971
- }
2972
- }
2973
-
2974
- /**
2975
- * @link http://redis.io/commands/persist
2976
- *
2977
- * @author Daniele Alessandri <suppakilla@gmail.com>
2978
- */
2979
- class KeyPersist extends Command
2980
- {
2981
- /**
2982
- * {@inheritdoc}
2983
- */
2984
- public function getId()
2985
- {
2986
- return 'PERSIST';
2987
- }
2988
- }
2989
-
2990
- /**
2991
- * @link http://redis.io/commands/spop
2992
- *
2993
- * @author Daniele Alessandri <suppakilla@gmail.com>
2994
- */
2995
- class SetPop extends Command
2996
- {
2997
- /**
2998
- * {@inheritdoc}
2999
- */
3000
- public function getId()
3001
- {
3002
- return 'SPOP';
3003
- }
3004
- }
3005
-
3006
- /**
3007
- * @link http://redis.io/commands/zincrby
3008
- *
3009
- * @author Daniele Alessandri <suppakilla@gmail.com>
3010
- */
3011
- class ZSetIncrementBy extends Command
3012
- {
3013
- /**
3014
- * {@inheritdoc}
3015
- */
3016
- public function getId()
3017
- {
3018
- return 'ZINCRBY';
3019
- }
3020
- }
3021
-
3022
- /**
3023
- * @link http://redis.io/commands/brpoplpush
3024
- *
3025
- * @author Daniele Alessandri <suppakilla@gmail.com>
3026
- */
3027
- class ListPopLastPushHeadBlocking extends Command
3028
- {
3029
- /**
3030
- * {@inheritdoc}
3031
- */
3032
- public function getId()
3033
- {
3034
- return 'BRPOPLPUSH';
3035
- }
3036
- }
3037
-
3038
- /**
3039
- * Defines a command whose keys can be prefixed.
3040
- *
3041
- * @author Daniele Alessandri <suppakilla@gmail.com>
3042
- */
3043
- interface PrefixableCommandInterface extends CommandInterface
3044
- {
3045
- /**
3046
- * Prefixes all the keys found in the arguments of the command.
3047
- *
3048
- * @param string $prefix String used to prefix the keys.
3049
- */
3050
- public function prefixKeys($prefix);
3051
- }
3052
-
3053
- /**
3054
- * @link http://redis.io/commands/zremrangebyrank
3055
- *
3056
- * @author Daniele Alessandri <suppakilla@gmail.com>
3057
- */
3058
- class ZSetRemoveRangeByRank extends Command
3059
- {
3060
- /**
3061
- * {@inheritdoc}
3062
- */
3063
- public function getId()
3064
- {
3065
- return 'ZREMRANGEBYRANK';
3066
- }
3067
- }
3068
-
3069
- /**
3070
- * @link http://redis.io/commands/zrevrangebyscore
3071
- *
3072
- * @author Daniele Alessandri <suppakilla@gmail.com>
3073
- */
3074
- class ZSetReverseRangeByScore extends ZSetRangeByScore
3075
- {
3076
- /**
3077
- * {@inheritdoc}
3078
- */
3079
- public function getId()
3080
- {
3081
- return 'ZREVRANGEBYSCORE';
3082
- }
3083
- }
3084
-
3085
- /**
3086
- * @link http://redis.io/commands/strlen
3087
- *
3088
- * @author Daniele Alessandri <suppakilla@gmail.com>
3089
- */
3090
- class StringStrlen extends Command
3091
- {
3092
- /**
3093
- * {@inheritdoc}
3094
- */
3095
- public function getId()
3096
- {
3097
- return 'STRLEN';
3098
- }
3099
- }
3100
-
3101
- /**
3102
- * @link http://redis.io/commands/scan
3103
- *
3104
- * @author Daniele Alessandri <suppakilla@gmail.com>
3105
- */
3106
- class KeyScan extends Command
3107
- {
3108
- /**
3109
- * {@inheritdoc}
3110
- */
3111
- public function getId()
3112
- {
3113
- return 'SCAN';
3114
- }
3115
-
3116
- /**
3117
- * {@inheritdoc}
3118
- */
3119
- protected function filterArguments(array $arguments)
3120
- {
3121
- if (count($arguments) === 2 && is_array($arguments[1])) {
3122
- $options = $this->prepareOptions(array_pop($arguments));
3123
- $arguments = array_merge($arguments, $options);
3124
- }
3125
-
3126
- return $arguments;
3127
- }
3128
-
3129
- /**
3130
- * Returns a list of options and modifiers compatible with Redis.
3131
- *
3132
- * @param array $options List of options.
3133
- *
3134
- * @return array
3135
- */
3136
- protected function prepareOptions($options)
3137
- {
3138
- $options = array_change_key_case($options, CASE_UPPER);
3139
- $normalized = array();
3140
-
3141
- if (!empty($options['MATCH'])) {
3142
- $normalized[] = 'MATCH';
3143
- $normalized[] = $options['MATCH'];
3144
- }
3145
-
3146
- if (!empty($options['COUNT'])) {
3147
- $normalized[] = 'COUNT';
3148
- $normalized[] = $options['COUNT'];
3149
- }
3150
-
3151
- return $normalized;
3152
- }
3153
- }
3154
-
3155
- /**
3156
- * @link http://redis.io/commands/object
3157
- *
3158
- * @author Daniele Alessandri <suppakilla@gmail.com>
3159
- */
3160
- class ServerObject extends Command
3161
- {
3162
- /**
3163
- * {@inheritdoc}
3164
- */
3165
- public function getId()
3166
- {
3167
- return 'OBJECT';
3168
- }
3169
- }
3170
-
3171
- /**
3172
- * @link http://redis.io/commands/incr
3173
- *
3174
- * @author Daniele Alessandri <suppakilla@gmail.com>
3175
- */
3176
- class StringIncrement extends Command
3177
- {
3178
- /**
3179
- * {@inheritdoc}
3180
- */
3181
- public function getId()
3182
- {
3183
- return 'INCR';
3184
- }
3185
- }
3186
-
3187
- /**
3188
- * @link http://redis.io/commands/restore
3189
- *
3190
- * @author Daniele Alessandri <suppakilla@gmail.com>
3191
- */
3192
- class KeyRestore extends Command
3193
- {
3194
- /**
3195
- * {@inheritdoc}
3196
- */
3197
- public function getId()
3198
- {
3199
- return 'RESTORE';
3200
- }
3201
- }
3202
-
3203
- /**
3204
- * @link http://redis.io/commands/zcount
3205
- *
3206
- * @author Daniele Alessandri <suppakilla@gmail.com>
3207
- */
3208
- class ZSetCount extends Command
3209
- {
3210
- /**
3211
- * {@inheritdoc}
3212
- */
3213
- public function getId()
3214
- {
3215
- return 'ZCOUNT';
3216
- }
3217
- }
3218
-
3219
- /**
3220
- * @link http://redis.io/commands/geohash
3221
- *
3222
- * @author Daniele Alessandri <suppakilla@gmail.com>
3223
- */
3224
- class GeospatialGeoHash extends Command
3225
- {
3226
- /**
3227
- * {@inheritdoc}
3228
- */
3229
- public function getId()
3230
- {
3231
- return 'GEOHASH';
3232
- }
3233
-
3234
- /**
3235
- * {@inheritdoc}
3236
- */
3237
- protected function filterArguments(array $arguments)
3238
- {
3239
- if (count($arguments) === 2 && is_array($arguments[1])) {
3240
- $members = array_pop($arguments);
3241
- $arguments = array_merge($arguments, $members);
3242
- }
3243
-
3244
- return $arguments;
3245
- }
3246
- }
3247
-
3248
- /**
3249
- * @link http://redis.io/commands/srem
3250
- *
3251
- * @author Daniele Alessandri <suppakilla@gmail.com>
3252
- */
3253
- class SetRemove extends Command
3254
- {
3255
- /**
3256
- * {@inheritdoc}
3257
- */
3258
- public function getId()
3259
- {
3260
- return 'SREM';
3261
- }
3262
-
3263
- /**
3264
- * {@inheritdoc}
3265
- */
3266
- protected function filterArguments(array $arguments)
3267
- {
3268
- return self::normalizeVariadic($arguments);
3269
- }
3270
- }
3271
-
3272
- /**
3273
- * @link http://redis.io/commands/time
3274
- *
3275
- * @author Daniele Alessandri <suppakilla@gmail.com>
3276
- */
3277
- class ServerTime extends Command
3278
- {
3279
- /**
3280
- * {@inheritdoc}
3281
- */
3282
- public function getId()
3283
- {
3284
- return 'TIME';
3285
- }
3286
- }
3287
-
3288
- /**
3289
- * @link http://redis.io/commands/ping
3290
- *
3291
- * @author Daniele Alessandri <suppakilla@gmail.com>
3292
- */
3293
- class ConnectionPing extends Command
3294
- {
3295
- /**
3296
- * {@inheritdoc}
3297
- */
3298
- public function getId()
3299
- {
3300
- return 'PING';
3301
- }
3302
- }
3303
-
3304
- /**
3305
- * @link http://redis.io/commands/shutdown
3306
- *
3307
- * @author Daniele Alessandri <suppakilla@gmail.com>
3308
- */
3309
- class ServerShutdown extends Command
3310
- {
3311
- /**
3312
- * {@inheritdoc}
3313
- */
3314
- public function getId()
3315
- {
3316
- return 'SHUTDOWN';
3317
- }
3318
- }
3319
-
3320
- /**
3321
- * @link http://redis.io/commands/pttl
3322
- *
3323
- * @author Daniele Alessandri <suppakilla@gmail.com>
3324
- */
3325
- class KeyPreciseTimeToLive extends KeyTimeToLive
3326
- {
3327
- /**
3328
- * {@inheritdoc}
3329
- */
3330
- public function getId()
3331
- {
3332
- return 'PTTL';
3333
- }
3334
- }
3335
-
3336
- /**
3337
- * @link http://redis.io/commands/getset
3338
- *
3339
- * @author Daniele Alessandri <suppakilla@gmail.com>
3340
- */
3341
- class StringGetSet extends Command
3342
- {
3343
- /**
3344
- * {@inheritdoc}
3345
- */
3346
- public function getId()
3347
- {
3348
- return 'GETSET';
3349
- }
3350
- }
3351
-
3352
- /**
3353
- * @link http://redis.io/commands/sdiffstore
3354
- *
3355
- * @author Daniele Alessandri <suppakilla@gmail.com>
3356
- */
3357
- class SetDifferenceStore extends SetIntersectionStore
3358
- {
3359
- /**
3360
- * {@inheritdoc}
3361
- */
3362
- public function getId()
3363
- {
3364
- return 'SDIFFSTORE';
3365
- }
3366
- }
3367
-
3368
- /**
3369
- * @link http://redis.io/commands/zinterstore
3370
- *
3371
- * @author Daniele Alessandri <suppakilla@gmail.com>
3372
- */
3373
- class ZSetIntersectionStore extends ZSetUnionStore
3374
- {
3375
- /**
3376
- * {@inheritdoc}
3377
- */
3378
- public function getId()
3379
- {
3380
- return 'ZINTERSTORE';
3381
- }
3382
- }
3383
-
3384
- /**
3385
- * @link http://redis.io/commands/lrange
3386
- *
3387
- * @author Daniele Alessandri <suppakilla@gmail.com>
3388
- */
3389
- class ListRange extends Command
3390
- {
3391
- /**
3392
- * {@inheritdoc}
3393
- */
3394
- public function getId()
3395
- {
3396
- return 'LRANGE';
3397
- }
3398
- }
3399
-
3400
- /**
3401
- * @link http://redis.io/commands/bitpos
3402
- *
3403
- * @author Daniele Alessandri <suppakilla@gmail.com>
3404
- */
3405
- class StringBitPos extends Command
3406
- {
3407
- /**
3408
- * {@inheritdoc}
3409
- */
3410
- public function getId()
3411
- {
3412
- return 'BITPOS';
3413
- }
3414
- }
3415
-
3416
- /**
3417
- * @link http://redis.io/commands/decr
3418
- *
3419
- * @author Daniele Alessandri <suppakilla@gmail.com>
3420
- */
3421
- class StringDecrement extends Command
3422
- {
3423
- /**
3424
- * {@inheritdoc}
3425
- */
3426
- public function getId()
3427
- {
3428
- return 'DECR';
3429
- }
3430
- }
3431
-
3432
- /**
3433
- * @link http://redis.io/commands/get
3434
- *
3435
- * @author Daniele Alessandri <suppakilla@gmail.com>
3436
- */
3437
- class StringGet extends Command
3438
- {
3439
- /**
3440
- * {@inheritdoc}
3441
- */
3442
- public function getId()
3443
- {
3444
- return 'GET';
3445
- }
3446
- }
3447
-
3448
- /**
3449
- * @link http://redis.io/commands/discard
3450
- *
3451
- * @author Daniele Alessandri <suppakilla@gmail.com>
3452
- */
3453
- class TransactionDiscard extends Command
3454
- {
3455
- /**
3456
- * {@inheritdoc}
3457
- */
3458
- public function getId()
3459
- {
3460
- return 'DISCARD';
3461
- }
3462
- }
3463
-
3464
- /**
3465
- * @link http://redis.io/commands/hmset
3466
- *
3467
- * @author Daniele Alessandri <suppakilla@gmail.com>
3468
- */
3469
- class HashSetMultiple extends Command
3470
- {
3471
- /**
3472
- * {@inheritdoc}
3473
- */
3474
- public function getId()
3475
- {
3476
- return 'HMSET';
3477
- }
3478
-
3479
- /**
3480
- * {@inheritdoc}
3481
- */
3482
- protected function filterArguments(array $arguments)
3483
- {
3484
- if (count($arguments) === 2 && is_array($arguments[1])) {
3485
- $flattenedKVs = array($arguments[0]);
3486
- $args = $arguments[1];
3487
-
3488
- foreach ($args as $k => $v) {
3489
- $flattenedKVs[] = $k;
3490
- $flattenedKVs[] = $v;
3491
- }
3492
-
3493
- return $flattenedKVs;
3494
- }
3495
-
3496
- return $arguments;
3497
- }
3498
- }
3499
-
3500
- /**
3501
- * @link http://redis.io/commands/zscore
3502
- *
3503
- * @author Daniele Alessandri <suppakilla@gmail.com>
3504
- */
3505
- class ZSetScore extends Command
3506
- {
3507
- /**
3508
- * {@inheritdoc}
3509
- */
3510
- public function getId()
3511
- {
3512
- return 'ZSCORE';
3513
- }
3514
- }
3515
-
3516
- /**
3517
- * @link http://redis.io/commands/bgsave
3518
- *
3519
- * @author Daniele Alessandri <suppakilla@gmail.com>
3520
- */
3521
- class ServerBackgroundSave extends Command
3522
- {
3523
- /**
3524
- * {@inheritdoc}
3525
- */
3526
- public function getId()
3527
- {
3528
- return 'BGSAVE';
3529
- }
3530
-
3531
- /**
3532
- * {@inheritdoc}
3533
- */
3534
- public function parseResponse($data)
3535
- {
3536
- return $data === 'Background saving started' ? true : $data;
3537
- }
3538
- }
3539
-
3540
- /**
3541
- * @link http://redis.io/commands/smove
3542
- *
3543
- * @author Daniele Alessandri <suppakilla@gmail.com>
3544
- */
3545
- class SetMove extends Command
3546
- {
3547
- /**
3548
- * {@inheritdoc}
3549
- */
3550
- public function getId()
3551
- {
3552
- return 'SMOVE';
3553
- }
3554
- }
3555
-
3556
- /**
3557
- * @link http://redis.io/commands/bitfield
3558
- *
3559
- * @author Daniele Alessandri <suppakilla@gmail.com>
3560
- */
3561
- class StringBitField extends Command
3562
- {
3563
- /**
3564
- * {@inheritdoc}
3565
- */
3566
- public function getId()
3567
- {
3568
- return 'BITFIELD';
3569
- }
3570
- }
3571
-
3572
- /**
3573
- * @link http://redis.io/commands/pfadd
3574
- *
3575
- * @author Daniele Alessandri <suppakilla@gmail.com>
3576
- */
3577
- class HyperLogLogAdd extends Command
3578
- {
3579
- /**
3580
- * {@inheritdoc}
3581
- */
3582
- public function getId()
3583
- {
3584
- return 'PFADD';
3585
- }
3586
-
3587
- /**
3588
- * {@inheritdoc}
3589
- */
3590
- protected function filterArguments(array $arguments)
3591
- {
3592
- return self::normalizeVariadic($arguments);
3593
- }
3594
- }
3595
-
3596
- /**
3597
- * @link http://redis.io/commands/hexists
3598
- *
3599
- * @author Daniele Alessandri <suppakilla@gmail.com>
3600
- */
3601
- class HashExists extends Command
3602
- {
3603
- /**
3604
- * {@inheritdoc}
3605
- */
3606
- public function getId()
3607
- {
3608
- return 'HEXISTS';
3609
- }
3610
- }
3611
-
3612
- /**
3613
- * @link http://redis.io/commands/linsert
3614
- *
3615
- * @author Daniele Alessandri <suppakilla@gmail.com>
3616
- */
3617
- class ListInsert extends Command
3618
- {
3619
- /**
3620
- * {@inheritdoc}
3621
- */
3622
- public function getId()
3623
- {
3624
- return 'LINSERT';
3625
- }
3626
- }
3627
-
3628
- /**
3629
- * @link http://redis.io/commands/hgetall
3630
- *
3631
- * @author Daniele Alessandri <suppakilla@gmail.com>
3632
- */
3633
- class HashGetAll extends Command
3634
- {
3635
- /**
3636
- * {@inheritdoc}
3637
- */
3638
- public function getId()
3639
- {
3640
- return 'HGETALL';
3641
- }
3642
-
3643
- /**
3644
- * {@inheritdoc}
3645
- */
3646
- public function parseResponse($data)
3647
- {
3648
- $result = array();
3649
-
3650
- for ($i = 0; $i < count($data); ++$i) {
3651
- $result[$data[$i]] = $data[++$i];
3652
- }
3653
-
3654
- return $result;
3655
- }
3656
- }
3657
-
3658
- /**
3659
- * Base class used to implement an higher level abstraction for commands based
3660
- * on Lua scripting with EVAL and EVALSHA.
3661
- *
3662
- * @link http://redis.io/commands/eval
3663
- *
3664
- * @author Daniele Alessandri <suppakilla@gmail.com>
3665
- */
3666
- abstract class ScriptCommand extends ServerEvalSHA
3667
- {
3668
- /**
3669
- * Gets the body of a Lua script.
3670
- *
3671
- * @return string
3672
- */
3673
- abstract public function getScript();
3674
-
3675
- /**
3676
- * Specifies the number of arguments that should be considered as keys.
3677
- *
3678
- * The default behaviour for the base class is to return 0 to indicate that
3679
- * all the elements of the arguments array should be considered as keys, but
3680
- * subclasses can enforce a static number of keys.
3681
- *
3682
- * @return int
3683
- */
3684
- protected function getKeysCount()
3685
- {
3686
- return 0;
3687
- }
3688
-
3689
- /**
3690
- * Returns the elements from the arguments that are identified as keys.
3691
- *
3692
- * @return array
3693
- */
3694
- public function getKeys()
3695
- {
3696
- return array_slice($this->getArguments(), 2, $this->getKeysCount());
3697
- }
3698
-
3699
- /**
3700
- * {@inheritdoc}
3701
- */
3702
- protected function filterArguments(array $arguments)
3703
- {
3704
- if (($numkeys = $this->getKeysCount()) && $numkeys < 0) {
3705
- $numkeys = count($arguments) + $numkeys;
3706
- }
3707
-
3708
- return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments);
3709
- }
3710
-
3711
- /**
3712
- * @return array
3713
- */
3714
- public function getEvalArguments()
3715
- {
3716
- $arguments = $this->getArguments();
3717
- $arguments[0] = $this->getScript();
3718
-
3719
- return $arguments;
3720
- }
3721
- }
3722
-
3723
- /**
3724
- * @link http://redis.io/commands/zrem
3725
- *
3726
- * @author Daniele Alessandri <suppakilla@gmail.com>
3727
- */
3728
- class ZSetRemove extends Command
3729
- {
3730
- /**
3731
- * {@inheritdoc}
3732
- */
3733
- public function getId()
3734
- {
3735
- return 'ZREM';
3736
- }
3737
-
3738
- /**
3739
- * {@inheritdoc}
3740
- */
3741
- protected function filterArguments(array $arguments)
3742
- {
3743
- return self::normalizeVariadic($arguments);
3744
- }
3745
- }
3746
-
3747
- /**
3748
- * @link http://redis.io/commands/slowlog
3749
- *
3750
- * @author Daniele Alessandri <suppakilla@gmail.com>
3751
- */
3752
- class ServerSlowlog extends Command
3753
- {
3754
- /**
3755
- * {@inheritdoc}
3756
- */
3757
- public function getId()
3758
- {
3759
- return 'SLOWLOG';
3760
- }
3761
-
3762
- /**
3763
- * {@inheritdoc}
3764
- */
3765
- public function parseResponse($data)
3766
- {
3767
- if (is_array($data)) {
3768
- $log = array();
3769
-
3770
- foreach ($data as $index => $entry) {
3771
- $log[$index] = array(
3772
- 'id' => $entry[0],
3773
- 'timestamp' => $entry[1],
3774
- 'duration' => $entry[2],
3775
- 'command' => $entry[3],
3776
- );
3777
- }
3778
-
3779
- return $log;
3780
- }
3781
-
3782
- return $data;
3783
- }
3784
- }
3785
-
3786
- /**
3787
- * @link http://redis.io/commands/zrevrangebylex
3788
- *
3789
- * @author Daniele Alessandri <suppakilla@gmail.com>
3790
- */
3791
- class ZSetReverseRangeByLex extends ZSetRangeByLex
3792
- {
3793
- /**
3794
- * {@inheritdoc}
3795
- */
3796
- public function getId()
3797
- {
3798
- return 'ZREVRANGEBYLEX';
3799
- }
3800
- }
3801
-
3802
- /**
3803
- * @link http://redis.io/commands/hlen
3804
- *
3805
- * @author Daniele Alessandri <suppakilla@gmail.com>
3806
- */
3807
- class HashLength extends Command
3808
- {
3809
- /**
3810
- * {@inheritdoc}
3811
- */
3812
- public function getId()
3813
- {
3814
- return 'HLEN';
3815
- }
3816
- }
3817
-
3818
- /**
3819
- * @link http://redis.io/commands/geoadd
3820
- *
3821
- * @author Daniele Alessandri <suppakilla@gmail.com>
3822
- */
3823
- class GeospatialGeoAdd extends Command
3824
- {
3825
- /**
3826
- * {@inheritdoc}
3827
- */
3828
- public function getId()
3829
- {
3830
- return 'GEOADD';
3831
- }
3832
-
3833
- /**
3834
- * {@inheritdoc}
3835
- */
3836
- protected function filterArguments(array $arguments)
3837
- {
3838
- if (count($arguments) === 2 && is_array($arguments[1])) {
3839
- foreach (array_pop($arguments) as $item) {
3840
- $arguments = array_merge($arguments, $item);
3841
- }
3842
- }
3843
-
3844
- return $arguments;
3845
- }
3846
- }
3847
-
3848
- /**
3849
- * @link http://redis.io/commands/client-list
3850
- * @link http://redis.io/commands/client-kill
3851
- * @link http://redis.io/commands/client-getname
3852
- * @link http://redis.io/commands/client-setname
3853
- *
3854
- * @author Daniele Alessandri <suppakilla@gmail.com>
3855
- */
3856
- class ServerClient extends Command
3857
- {
3858
- /**
3859
- * {@inheritdoc}
3860
- */
3861
- public function getId()
3862
- {
3863
- return 'CLIENT';
3864
- }
3865
-
3866
- /**
3867
- * {@inheritdoc}
3868
- */
3869
- public function parseResponse($data)
3870
- {
3871
- $args = array_change_key_case($this->getArguments(), CASE_UPPER);
3872
-
3873
- switch (strtoupper($args[0])) {
3874
- case 'LIST':
3875
- return $this->parseClientList($data);
3876
- case 'KILL':
3877
- case 'GETNAME':
3878
- case 'SETNAME':
3879
- default:
3880
- return $data;
3881
- }
3882
- }
3883
-
3884
- /**
3885
- * Parses the response to CLIENT LIST and returns a structured list.
3886
- *
3887
- * @param string $data Response buffer.
3888
- *
3889
- * @return array
3890
- */
3891
- protected function parseClientList($data)
3892
- {
3893
- $clients = array();
3894
-
3895
- foreach (explode("\n", $data, -1) as $clientData) {
3896
- $client = array();
3897
-
3898
- foreach (explode(' ', $clientData) as $kv) {
3899
- @list($k, $v) = explode('=', $kv);
3900
- $client[$k] = $v;
3901
- }
3902
-
3903
- $clients[] = $client;
3904
- }
3905
-
3906
- return $clients;
3907
- }
3908
- }
3909
-
3910
- /**
3911
- * @link http://redis.io/commands/pubsub
3912
- *
3913
- * @author Daniele Alessandri <suppakilla@gmail.com>
3914
- */
3915
- class PubSubPubsub extends Command
3916
- {
3917
- /**
3918
- * {@inheritdoc}
3919
- */
3920
- public function getId()
3921
- {
3922
- return 'PUBSUB';
3923
- }
3924
-
3925
- /**
3926
- * {@inheritdoc}
3927
- */
3928
- public function parseResponse($data)
3929
- {
3930
- switch (strtolower($this->getArgument(0))) {
3931
- case 'numsub':
3932
- return self::processNumsub($data);
3933
-
3934
- default:
3935
- return $data;
3936
- }
3937
- }
3938
-
3939
- /**
3940
- * Returns the processed response to PUBSUB NUMSUB.
3941
- *
3942
- * @param array $channels List of channels
3943
- *
3944
- * @return array
3945
- */
3946
- protected static function processNumsub(array $channels)
3947
- {
3948
- $processed = array();
3949
- $count = count($channels);
3950
-
3951
- for ($i = 0; $i < $count; ++$i) {
3952
- $processed[$channels[$i]] = $channels[++$i];
3953
- }
3954
-
3955
- return $processed;
3956
- }
3957
- }
3958
-
3959
- /**
3960
- * @link http://redis.io/commands/lset
3961
- *
3962
- * @author Daniele Alessandri <suppakilla@gmail.com>
3963
- */
3964
- class ListSet extends Command
3965
- {
3966
- /**
3967
- * {@inheritdoc}
3968
- */
3969
- public function getId()
3970
- {
3971
- return 'LSET';
3972
- }
3973
- }
3974
-
3975
- /**
3976
- * @link http://redis.io/commands/lpush
3977
- *
3978
- * @author Daniele Alessandri <suppakilla@gmail.com>
3979
- */
3980
- class ListPushHead extends ListPushTail
3981
- {
3982
- /**
3983
- * {@inheritdoc}
3984
- */
3985
- public function getId()
3986
- {
3987
- return 'LPUSH';
3988
- }
3989
- }
3990
-
3991
- /**
3992
- * @link http://redis.io/commands/sdiff
3993
- *
3994
- * @author Daniele Alessandri <suppakilla@gmail.com>
3995
- */
3996
- class SetDifference extends SetIntersection
3997
- {
3998
- /**
3999
- * {@inheritdoc}
4000
- */
4001
- public function getId()
4002
- {
4003
- return 'SDIFF';
4004
- }
4005
- }
4006
-
4007
- /**
4008
- * @link http://redis.io/commands/geodist
4009
- *
4010
- * @author Daniele Alessandri <suppakilla@gmail.com>
4011
- */
4012
- class GeospatialGeoDist extends Command
4013
- {
4014
- /**
4015
- * {@inheritdoc}
4016
- */
4017
- public function getId()
4018
- {
4019
- return 'GEODIST';
4020
- }
4021
- }
4022
-
4023
- /**
4024
- * @link http://redis.io/commands/mget
4025
- *
4026
- * @author Daniele Alessandri <suppakilla@gmail.com>
4027
- */
4028
- class StringGetMultiple extends Command
4029
- {
4030
- /**
4031
- * {@inheritdoc}
4032
- */
4033
- public function getId()
4034
- {
4035
- return 'MGET';
4036
- }
4037
-
4038
- /**
4039
- * {@inheritdoc}
4040
- */
4041
- protected function filterArguments(array $arguments)
4042
- {
4043
- return self::normalizeArguments($arguments);
4044
- }
4045
- }
4046
-
4047
- /**
4048
- * @link http://redis.io/commands/exists
4049
- *
4050
- * @author Daniele Alessandri <suppakilla@gmail.com>
4051
- */
4052
- class KeyExists extends Command
4053
- {
4054
- /**
4055
- * {@inheritdoc}
4056
- */
4057
- public function getId()
4058
- {
4059
- return 'EXISTS';
4060
- }
4061
- }
4062
-
4063
- /* --------------------------------------------------------------------------- */
4064
-
4065
- namespace Predis\Connection;
4066
-
4067
- use Predis\Command\CommandInterface;
4068
- use Predis\Response\Error as ErrorResponse;
4069
- use Predis\Response\ErrorInterface as ErrorResponseInterface;
4070
- use Predis\Response\Status as StatusResponse;
4071
- use Predis\Command\RawCommand;
4072
- use Predis\NotSupportedException;
4073
- use Predis\CommunicationException;
4074
- use Predis\Protocol\ProtocolException;
4075
- use Predis\Protocol\ProtocolProcessorInterface;
4076
- use Predis\Protocol\Text\ProtocolProcessor as TextProtocolProcessor;
4077
-
4078
- /**
4079
- * Defines a connection object used to communicate with one or multiple
4080
- * Redis servers.
4081
- *
4082
- * @author Daniele Alessandri <suppakilla@gmail.com>
4083
- */
4084
- interface ConnectionInterface
4085
- {
4086
- /**
4087
- * Opens the connection to Redis.
4088
- */
4089
- public function connect();
4090
-
4091
- /**
4092
- * Closes the connection to Redis.
4093
- */
4094
- public function disconnect();
4095
-
4096
- /**
4097
- * Checks if the connection to Redis is considered open.
4098
- *
4099
- * @return bool
4100
- */
4101
- public function isConnected();
4102
-
4103
- /**
4104
- * Writes the request for the given command over the connection.
4105
- *
4106
- * @param CommandInterface $command Command instance.
4107
- */
4108
- public function writeRequest(CommandInterface $command);
4109
-
4110
- /**
4111
- * Reads the response to the given command from the connection.
4112
- *
4113
- * @param CommandInterface $command Command instance.
4114
- *
4115
- * @return mixed
4116
- */
4117
- public function readResponse(CommandInterface $command);
4118
-
4119
- /**
4120
- * Writes a request for the given command over the connection and reads back
4121
- * the response returned by Redis.
4122
- *
4123
- * @param CommandInterface $command Command instance.
4124
- *
4125
- * @return mixed
4126
- */
4127
- public function executeCommand(CommandInterface $command);
4128
- }
4129
-
4130
- /**
4131
- * Defines a connection used to communicate with a single Redis node.
4132
- *
4133
- * @author Daniele Alessandri <suppakilla@gmail.com>
4134
- */
4135
- interface NodeConnectionInterface extends ConnectionInterface
4136
- {
4137
- /**
4138
- * Returns a string representation of the connection.
4139
- *
4140
- * @return string
4141
- */
4142
- public function __toString();
4143
-
4144
- /**
4145
- * Returns the underlying resource used to communicate with Redis.
4146
- *
4147
- * @return mixed
4148
- */
4149
- public function getResource();
4150
-
4151
- /**
4152
- * Returns the parameters used to initialize the connection.
4153
- *
4154
- * @return ParametersInterface
4155
- */
4156
- public function getParameters();
4157
-
4158
- /**
4159
- * Pushes the given command into a queue of commands executed when
4160
- * establishing the actual connection to Redis.
4161
- *
4162
- * @param CommandInterface $command Instance of a Redis command.
4163
- */
4164
- public function addConnectCommand(CommandInterface $command);
4165
-
4166
- /**
4167
- * Reads a response from the server.
4168
- *
4169
- * @return mixed
4170
- */
4171
- public function read();
4172
- }
4173
-
4174
- /**
4175
- * Defines a virtual connection composed of multiple connection instances to
4176
- * single Redis nodes.
4177
- *
4178
- * @author Daniele Alessandri <suppakilla@gmail.com>
4179
- */
4180
- interface AggregateConnectionInterface extends ConnectionInterface
4181
- {
4182
- /**
4183
- * Adds a connection instance to the aggregate connection.
4184
- *
4185
- * @param NodeConnectionInterface $connection Connection instance.
4186
- */
4187
- public function add(NodeConnectionInterface $connection);
4188
-
4189
- /**
4190
- * Removes the specified connection instance from the aggregate connection.
4191
- *
4192
- * @param NodeConnectionInterface $connection Connection instance.
4193
- *
4194
- * @return bool Returns true if the connection was in the pool.
4195
- */
4196
- public function remove(NodeConnectionInterface $connection);
4197
-
4198
- /**
4199
- * Returns the connection instance in charge for the given command.
4200
- *
4201
- * @param CommandInterface $command Command instance.
4202
- *
4203
- * @return NodeConnectionInterface
4204
- */
4205
- public function getConnection(CommandInterface $command);
4206
-
4207
- /**
4208
- * Returns a connection instance from the aggregate connection by its alias.
4209
- *
4210
- * @param string $connectionID Connection alias.
4211
- *
4212
- * @return NodeConnectionInterface|null
4213
- */
4214
- public function getConnectionById($connectionID);
4215
- }
4216
-
4217
- /**
4218
- * Base class with the common logic used by connection classes to communicate
4219
- * with Redis.
4220
- *
4221
- * @author Daniele Alessandri <suppakilla@gmail.com>
4222
- */
4223
- abstract class AbstractConnection implements NodeConnectionInterface
4224
- {
4225
- private $resource;
4226
- private $cachedId;
4227
-
4228
- protected $parameters;
4229
- protected $initCommands = array();
4230
-
4231
- /**
4232
- * @param ParametersInterface $parameters Initialization parameters for the connection.
4233
- */
4234
- public function __construct(ParametersInterface $parameters)
4235
- {
4236
- $this->parameters = $this->assertParameters($parameters);
4237
- }
4238
-
4239
- /**
4240
- * Disconnects from the server and destroys the underlying resource when
4241
- * PHP's garbage collector kicks in.
4242
- */
4243
- public function __destruct()
4244
- {
4245
- $this->disconnect();
4246
- }
4247
-
4248
- /**
4249
- * Checks some of the parameters used to initialize the connection.
4250
- *
4251
- * @param ParametersInterface $parameters Initialization parameters for the connection.
4252
- *
4253
- * @throws \InvalidArgumentException
4254
- *
4255
- * @return ParametersInterface
4256
- */
4257
- abstract protected function assertParameters(ParametersInterface $parameters);
4258
-
4259
- /**
4260
- * Creates the underlying resource used to communicate with Redis.
4261
- *
4262
- * @return mixed
4263
- */
4264
- abstract protected function createResource();
4265
-
4266
- /**
4267
- * {@inheritdoc}
4268
- */
4269
- public function isConnected()
4270
- {
4271
- return isset($this->resource);
4272
- }
4273
-
4274
- /**
4275
- * {@inheritdoc}
4276
- */
4277
- public function connect()
4278
- {
4279
- if (!$this->isConnected()) {
4280
- $this->resource = $this->createResource();
4281
-
4282
- return true;
4283
- }
4284
-
4285
- return false;
4286
- }
4287
-
4288
- /**
4289
- * {@inheritdoc}
4290
- */
4291
- public function disconnect()
4292
- {
4293
- unset($this->resource);
4294
- }
4295
-
4296
- /**
4297
- * {@inheritdoc}
4298
- */
4299
- public function addConnectCommand(CommandInterface $command)
4300
- {
4301
- $this->initCommands[] = $command;
4302
- }
4303
-
4304
- /**
4305
- * {@inheritdoc}
4306
- */
4307
- public function executeCommand(CommandInterface $command)
4308
- {
4309
- $this->writeRequest($command);
4310
-
4311
- return $this->readResponse($command);
4312
- }
4313
-
4314
- /**
4315
- * {@inheritdoc}
4316
- */
4317
- public function readResponse(CommandInterface $command)
4318
- {
4319
- return $this->read();
4320
- }
4321
-
4322
- /**
4323
- * Helper method that returns an exception message augmented with useful
4324
- * details from the connection parameters.
4325
- *
4326
- * @param string $message Error message.
4327
- *
4328
- * @return string
4329
- */
4330
- private function createExceptionMessage($message)
4331
- {
4332
- $parameters = $this->parameters;
4333
-
4334
- if ($parameters->scheme === 'unix') {
4335
- return "$message [$parameters->scheme:$parameters->path]";
4336
- }
4337
-
4338
- if (filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
4339
- return "$message [$parameters->scheme://[$parameters->host]:$parameters->port]";
4340
- }
4341
-
4342
- return "$message [$parameters->scheme://$parameters->host:$parameters->port]";
4343
- }
4344
-
4345
- /**
4346
- * Helper method to handle connection errors.
4347
- *
4348
- * @param string $message Error message.
4349
- * @param int $code Error code.
4350
- */
4351
- protected function onConnectionError($message, $code = null)
4352
- {
4353
- CommunicationException::handle(
4354
- new ConnectionException($this, static::createExceptionMessage($message), $code)
4355
- );
4356
- }
4357
-
4358
- /**
4359
- * Helper method to handle protocol errors.
4360
- *
4361
- * @param string $message Error message.
4362
- */
4363
- protected function onProtocolError($message)
4364
- {
4365
- CommunicationException::handle(
4366
- new ProtocolException($this, static::createExceptionMessage($message))
4367
- );
4368
- }
4369
-
4370
- /**
4371
- * {@inheritdoc}
4372
- */
4373
- public function getResource()
4374
- {
4375
- if (isset($this->resource)) {
4376
- return $this->resource;
4377
- }
4378
-
4379
- $this->connect();
4380
-
4381
- return $this->resource;
4382
- }
4383
-
4384
- /**
4385
- * {@inheritdoc}
4386
- */
4387
- public function getParameters()
4388
- {
4389
- return $this->parameters;
4390
- }
4391
-
4392
- /**
4393
- * Gets an identifier for the connection.
4394
- *
4395
- * @return string
4396
- */
4397
- protected function getIdentifier()
4398
- {
4399
- if ($this->parameters->scheme === 'unix') {
4400
- return $this->parameters->path;
4401
- }
4402
-
4403
- return "{$this->parameters->host}:{$this->parameters->port}";
4404
- }
4405
-
4406
- /**
4407
- * {@inheritdoc}
4408
- */
4409
- public function __toString()
4410
- {
4411
- if (!isset($this->cachedId)) {
4412
- $this->cachedId = $this->getIdentifier();
4413
- }
4414
-
4415
- return $this->cachedId;
4416
- }
4417
-
4418
- /**
4419
- * {@inheritdoc}
4420
- */
4421
- public function __sleep()
4422
- {
4423
- return array('parameters', 'initCommands');
4424
- }
4425
- }
4426
-
4427
- /**
4428
- * Standard connection to Redis servers implemented on top of PHP's streams.
4429
- * The connection parameters supported by this class are:.
4430
- *
4431
- * - scheme: it can be either 'redis', 'tcp', 'rediss', 'tls' or 'unix'.
4432
- * - host: hostname or IP address of the server.
4433
- * - port: TCP port of the server.
4434
- * - path: path of a UNIX domain socket when scheme is 'unix'.
4435
- * - timeout: timeout to perform the connection (default is 5 seconds).
4436
- * - read_write_timeout: timeout of read / write operations.
4437
- * - async_connect: performs the connection asynchronously.
4438
- * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing.
4439
- * - persistent: the connection is left intact after a GC collection.
4440
- * - ssl: context options array (see http://php.net/manual/en/context.ssl.php)
4441
- *
4442
- * @author Daniele Alessandri <suppakilla@gmail.com>
4443
- */
4444
- class StreamConnection extends AbstractConnection
4445
- {
4446
- /**
4447
- * Disconnects from the server and destroys the underlying resource when the
4448
- * garbage collector kicks in only if the connection has not been marked as
4449
- * persistent.
4450
- */
4451
- public function __destruct()
4452
- {
4453
- if (isset($this->parameters->persistent) && $this->parameters->persistent) {
4454
- return;
4455
- }
4456
-
4457
- $this->disconnect();
4458
- }
4459
-
4460
- /**
4461
- * {@inheritdoc}
4462
- */
4463
- protected function assertParameters(ParametersInterface $parameters)
4464
- {
4465
- switch ($parameters->scheme) {
4466
- case 'tcp':
4467
- case 'redis':
4468
- case 'unix':
4469
- break;
4470
-
4471
- case 'tls':
4472
- case 'rediss':
4473
- $this->assertSslSupport($parameters);
4474
- break;
4475
-
4476
- default:
4477
- throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
4478
- }
4479
-
4480
- return $parameters;
4481
- }
4482
-
4483
- /**
4484
- * Checks needed conditions for SSL-encrypted connections.
4485
- *
4486
- * @param ParametersInterface $parameters Initialization parameters for the connection.
4487
- *
4488
- * @throws \InvalidArgumentException
4489
- */
4490
- protected function assertSslSupport(ParametersInterface $parameters)
4491
- {
4492
- if (
4493
- filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN) &&
4494
- version_compare(PHP_VERSION, '7.0.0beta') < 0
4495
- ) {
4496
- throw new \InvalidArgumentException('Persistent SSL connections require PHP >= 7.0.0.');
4497
- }
4498
- }
4499
-
4500
- /**
4501
- * {@inheritdoc}
4502
- */
4503
- protected function createResource()
4504
- {
4505
- switch ($this->parameters->scheme) {
4506
- case 'tcp':
4507
- case 'redis':
4508
- return $this->tcpStreamInitializer($this->parameters);
4509
-
4510
- case 'unix':
4511
- return $this->unixStreamInitializer($this->parameters);
4512
-
4513
- case 'tls':
4514
- case 'rediss':
4515
- return $this->tlsStreamInitializer($this->parameters);
4516
-
4517
- default:
4518
- throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'.");
4519
- }
4520
- }
4521
-
4522
- /**
4523
- * Creates a connected stream socket resource.
4524
- *
4525
- * @param ParametersInterface $parameters Connection parameters.
4526
- * @param string $address Address for stream_socket_client().
4527
- * @param int $flags Flags for stream_socket_client().
4528
- *
4529
- * @return resource
4530
- */
4531
- protected function createStreamSocket(ParametersInterface $parameters, $address, $flags)
4532
- {
4533
- $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
4534
-
4535
- if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) {
4536
- $this->onConnectionError(trim($errstr), $errno);
4537
- }
4538
-
4539
- if (isset($parameters->read_write_timeout)) {
4540
- $rwtimeout = (float) $parameters->read_write_timeout;
4541
- $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1;
4542
- $timeoutSeconds = floor($rwtimeout);
4543
- $timeoutUSeconds = ($rwtimeout - $timeoutSeconds) * 1000000;
4544
- stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds);
4545
- }
4546
-
4547
- if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) {
4548
- $socket = socket_import_stream($resource);
4549
- socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
4550
- }
4551
-
4552
- return $resource;
4553
- }
4554
-
4555
- /**
4556
- * Initializes a TCP stream resource.
4557
- *
4558
- * @param ParametersInterface $parameters Initialization parameters for the connection.
4559
- *
4560
- * @return resource
4561
- */
4562
- protected function tcpStreamInitializer(ParametersInterface $parameters)
4563
- {
4564
- if (!filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
4565
- $address = "tcp://$parameters->host:$parameters->port";
4566
- } else {
4567
- $address = "tcp://[$parameters->host]:$parameters->port";
4568
- }
4569
-
4570
- $flags = STREAM_CLIENT_CONNECT;
4571
-
4572
- if (isset($parameters->async_connect) && $parameters->async_connect) {
4573
- $flags |= STREAM_CLIENT_ASYNC_CONNECT;
4574
- }
4575
-
4576
- if (isset($parameters->persistent)) {
4577
- if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
4578
- $flags |= STREAM_CLIENT_PERSISTENT;
4579
-
4580
- if ($persistent === null) {
4581
- $address = "{$address}/{$parameters->persistent}";
4582
- }
4583
- }
4584
- }
4585
-
4586
- $resource = $this->createStreamSocket($parameters, $address, $flags);
4587
-
4588
- return $resource;
4589
- }
4590
-
4591
- /**
4592
- * Initializes a UNIX stream resource.
4593
- *
4594
- * @param ParametersInterface $parameters Initialization parameters for the connection.
4595
- *
4596
- * @return resource
4597
- */
4598
- protected function unixStreamInitializer(ParametersInterface $parameters)
4599
- {
4600
- if (!isset($parameters->path)) {
4601
- throw new \InvalidArgumentException('Missing UNIX domain socket path.');
4602
- }
4603
-
4604
- $flags = STREAM_CLIENT_CONNECT;
4605
-
4606
- if (isset($parameters->persistent)) {
4607
- if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
4608
- $flags |= STREAM_CLIENT_PERSISTENT;
4609
-
4610
- if ($persistent === null) {
4611
- throw new \InvalidArgumentException(
4612
- 'Persistent connection IDs are not supported when using UNIX domain sockets.'
4613
- );
4614
- }
4615
- }
4616
- }
4617
-
4618
- $resource = $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags);
4619
-
4620
- return $resource;
4621
- }
4622
-
4623
- /**
4624
- * Initializes a SSL-encrypted TCP stream resource.
4625
- *
4626
- * @param ParametersInterface $parameters Initialization parameters for the connection.
4627
- *
4628
- * @return resource
4629
- */
4630
- protected function tlsStreamInitializer(ParametersInterface $parameters)
4631
- {
4632
- $resource = $this->tcpStreamInitializer($parameters);
4633
- $metadata = stream_get_meta_data($resource);
4634
-
4635
- // Detect if crypto mode is already enabled for this stream (PHP >= 7.0.0).
4636
- if (isset($metadata['crypto'])) {
4637
- return $resource;
4638
- }
4639
-
4640
- if (is_array($parameters->ssl)) {
4641
- $options = $parameters->ssl;
4642
- } else {
4643
- $options = array();
4644
- }
4645
-
4646
- if (!isset($options['crypto_type'])) {
4647
- $options['crypto_type'] = STREAM_CRYPTO_METHOD_TLS_CLIENT;
4648
- }
4649
-
4650
- if (!stream_context_set_option($resource, array('ssl' => $options))) {
4651
- $this->onConnectionError('Error while setting SSL context options');
4652
- }
4653
-
4654
- if (!stream_socket_enable_crypto($resource, true, $options['crypto_type'])) {
4655
- $this->onConnectionError('Error while switching to encrypted communication');
4656
- }
4657
-
4658
- return $resource;
4659
- }
4660
-
4661
- /**
4662
- * {@inheritdoc}
4663
- */
4664
- public function connect()
4665
- {
4666
- if (parent::connect() && $this->initCommands) {
4667
- foreach ($this->initCommands as $command) {
4668
- $response = $this->executeCommand($command);
4669
-
4670
- if ($response instanceof ErrorResponseInterface) {
4671
- $this->onConnectionError("`{$command->getId()}` failed: $response", 0);
4672
- }
4673
- }
4674
- }
4675
- }
4676
-
4677
- /**
4678
- * {@inheritdoc}
4679
- */
4680
- public function disconnect()
4681
- {
4682
- if ($this->isConnected()) {
4683
- fclose($this->getResource());
4684
- parent::disconnect();
4685
- }
4686
- }
4687
-
4688
- /**
4689
- * Performs a write operation over the stream of the buffer containing a
4690
- * command serialized with the Redis wire protocol.
4691
- *
4692
- * @param string $buffer Representation of a command in the Redis wire protocol.
4693
- */
4694
- protected function write($buffer)
4695
- {
4696
- $socket = $this->getResource();
4697
-
4698
- while (($length = strlen($buffer)) > 0) {
4699
- $written = @fwrite($socket, $buffer);
4700
-
4701
- if ($length === $written) {
4702
- return;
4703
- }
4704
-
4705
- if ($written === false || $written === 0) {
4706
- $this->onConnectionError('Error while writing bytes to the server.');
4707
- }
4708
-
4709
- $buffer = substr($buffer, $written);
4710
- }
4711
- }
4712
-
4713
- /**
4714
- * {@inheritdoc}
4715
- */
4716
- public function read()
4717
- {
4718
- $socket = $this->getResource();
4719
- $chunk = fgets($socket);
4720
-
4721
- if ($chunk === false || $chunk === '') {
4722
- $this->onConnectionError('Error while reading line from the server.');
4723
- }
4724
-
4725
- $prefix = $chunk[0];
4726
- $payload = substr($chunk, 1, -2);
4727
-
4728
- switch ($prefix) {
4729
- case '+':
4730
- return StatusResponse::get($payload);
4731
-
4732
- case '$':
4733
- $size = (int) $payload;
4734
-
4735
- if ($size === -1) {
4736
- return;
4737
- }
4738
-
4739
- $bulkData = '';
4740
- $bytesLeft = ($size += 2);
4741
-
4742
- do {
4743
- $chunk = fread($socket, min($bytesLeft, 4096));
4744
-
4745
- if ($chunk === false || $chunk === '') {
4746
- $this->onConnectionError('Error while reading bytes from the server.');
4747
- }
4748
-
4749
- $bulkData .= $chunk;
4750
- $bytesLeft = $size - strlen($bulkData);
4751
- } while ($bytesLeft > 0);
4752
-
4753
- return substr($bulkData, 0, -2);
4754
-
4755
- case '*':
4756
- $count = (int) $payload;
4757
-
4758
- if ($count === -1) {
4759
- return;
4760
- }
4761
-
4762
- $multibulk = array();
4763
-
4764
- for ($i = 0; $i < $count; ++$i) {
4765
- $multibulk[$i] = $this->read();
4766
- }
4767
-
4768
- return $multibulk;
4769
-
4770
- case ':':
4771
- $integer = (int) $payload;
4772
- return $integer == $payload ? $integer : $payload;
4773
-
4774
- case '-':
4775
- return new ErrorResponse($payload);
4776
-
4777
- default:
4778
- $this->onProtocolError("Unknown response prefix: '$prefix'.");
4779
-
4780
- return;
4781
- }
4782
- }
4783
-
4784
- /**
4785
- * {@inheritdoc}
4786
- */
4787
- public function writeRequest(CommandInterface $command)
4788
- {
4789
- $commandID = $command->getId();
4790
- $arguments = $command->getArguments();
4791
-
4792
- $cmdlen = strlen($commandID);
4793
- $reqlen = count($arguments) + 1;
4794
-
4795
- $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
4796
-
4797
- foreach ($arguments as $argument) {
4798
- $arglen = strlen($argument);
4799
- $buffer .= "\${$arglen}\r\n{$argument}\r\n";
4800
- }
4801
-
4802
- $this->write($buffer);
4803
- }
4804
- }
4805
-
4806
- /**
4807
- * Interface for classes providing a factory of connections to Redis nodes.
4808
- *
4809
- * @author Daniele Alessandri <suppakilla@gmail.com>
4810
- */
4811
- interface FactoryInterface
4812
- {
4813
- /**
4814
- * Defines or overrides the connection class identified by a scheme prefix.
4815
- *
4816
- * @param string $scheme Target connection scheme.
4817
- * @param mixed $initializer Fully-qualified name of a class or a callable for lazy initialization.
4818
- */
4819
- public function define($scheme, $initializer);
4820
-
4821
- /**
4822
- * Undefines the connection identified by a scheme prefix.
4823
- *
4824
- * @param string $scheme Target connection scheme.
4825
- */
4826
- public function undefine($scheme);
4827
-
4828
- /**
4829
- * Creates a new connection object.
4830
- *
4831
- * @param mixed $parameters Initialization parameters for the connection.
4832
- *
4833
- * @return NodeConnectionInterface
4834
- */
4835
- public function create($parameters);
4836
-
4837
- /**
4838
- * Aggregates single connections into an aggregate connection instance.
4839
- *
4840
- * @param AggregateConnectionInterface $aggregate Aggregate connection instance.
4841
- * @param array $parameters List of parameters for each connection.
4842
- */
4843
- public function aggregate(AggregateConnectionInterface $aggregate, array $parameters);
4844
- }
4845
-
4846
- /**
4847
- * Defines a connection to communicate with a single Redis server that leverages
4848
- * an external protocol processor to handle pluggable protocol handlers.
4849
- *
4850
- * @author Daniele Alessandri <suppakilla@gmail.com>
4851
- */
4852
- interface CompositeConnectionInterface extends NodeConnectionInterface
4853
- {
4854
- /**
4855
- * Returns the protocol processor used by the connection.
4856
- */
4857
- public function getProtocol();
4858
-
4859
- /**
4860
- * Writes the buffer containing over the connection.
4861
- *
4862
- * @param string $buffer String buffer to be sent over the connection.
4863
- */
4864
- public function writeBuffer($buffer);
4865
-
4866
- /**
4867
- * Reads the given number of bytes from the connection.
4868
- *
4869
- * @param int $length Number of bytes to read from the connection.
4870
- *
4871
- * @return string
4872
- */
4873
- public function readBuffer($length);
4874
-
4875
- /**
4876
- * Reads a line from the connection.
4877
- *
4878
- * @param string
4879
- */
4880
- public function readLine();
4881
- }
4882
-
4883
- /**
4884
- * Interface defining a container for connection parameters.
4885
- *
4886
- * The actual list of connection parameters depends on the features supported by
4887
- * each connection backend class (please refer to their specific documentation),
4888
- * but the most common parameters used through the library are:
4889
- *
4890
- * @property-read string scheme Connection scheme, such as 'tcp' or 'unix'.
4891
- * @property-read string host IP address or hostname of Redis.
4892
- * @property-read int port TCP port on which Redis is listening to.
4893
- * @property-read string path Path of a UNIX domain socket file.
4894
- * @property-read string alias Alias for the connection.
4895
- * @property-read float timeout Timeout for the connect() operation.
4896
- * @property-read float read_write_timeout Timeout for read() and write() operations.
4897
- * @property-read bool async_connect Performs the connect() operation asynchronously.
4898
- * @property-read bool tcp_nodelay Toggles the Nagle's algorithm for coalescing.
4899
- * @property-read bool persistent Leaves the connection open after a GC collection.
4900
- * @property-read string password Password to access Redis (see the AUTH command).
4901
- * @property-read string database Database index (see the SELECT command).
4902
- *
4903
- * @author Daniele Alessandri <suppakilla@gmail.com>
4904
- */
4905
- interface ParametersInterface
4906
- {
4907
- /**
4908
- * Checks if the specified parameters is set.
4909
- *
4910
- * @param string $parameter Name of the parameter.
4911
- *
4912
- * @return bool
4913
- */
4914
- public function __isset($parameter);
4915
-
4916
- /**
4917
- * Returns the value of the specified parameter.
4918
- *
4919
- * @param string $parameter Name of the parameter.
4920
- *
4921
- * @return mixed|null
4922
- */
4923
- public function __get($parameter);
4924
-
4925
- /**
4926
- * Returns an array representation of the connection parameters.
4927
- *
4928
- * @return array
4929
- */
4930
- public function toArray();
4931
- }
4932
-
4933
- /**
4934
- * Standard connection factory for creating connections to Redis nodes.
4935
- *
4936
- * @author Daniele Alessandri <suppakilla@gmail.com>
4937
- */
4938
- class Factory implements FactoryInterface
4939
- {
4940
- private $defaults = array();
4941
-
4942
- protected $schemes = array(
4943
- 'tcp' => 'Predis\Connection\StreamConnection',
4944
- 'unix' => 'Predis\Connection\StreamConnection',
4945
- 'tls' => 'Predis\Connection\StreamConnection',
4946
- 'redis' => 'Predis\Connection\StreamConnection',
4947
- 'rediss' => 'Predis\Connection\StreamConnection',
4948
- 'http' => 'Predis\Connection\WebdisConnection',
4949
- );
4950
-
4951
- /**
4952
- * Checks if the provided argument represents a valid connection class
4953
- * implementing Predis\Connection\NodeConnectionInterface. Optionally,
4954
- * callable objects are used for lazy initialization of connection objects.
4955
- *
4956
- * @param mixed $initializer FQN of a connection class or a callable for lazy initialization.
4957
- *
4958
- * @throws \InvalidArgumentException
4959
- *
4960
- * @return mixed
4961
- */
4962
- protected function checkInitializer($initializer)
4963
- {
4964
- if (is_callable($initializer)) {
4965
- return $initializer;
4966
- }
4967
-
4968
- $class = new \ReflectionClass($initializer);
4969
-
4970
- if (!$class->isSubclassOf('Predis\Connection\NodeConnectionInterface')) {
4971
- throw new \InvalidArgumentException(
4972
- 'A connection initializer must be a valid connection class or a callable object.'
4973
- );
4974
- }
4975
-
4976
- return $initializer;
4977
- }
4978
-
4979
- /**
4980
- * {@inheritdoc}
4981
- */
4982
- public function define($scheme, $initializer)
4983
- {
4984
- $this->schemes[$scheme] = $this->checkInitializer($initializer);
4985
- }
4986
-
4987
- /**
4988
- * {@inheritdoc}
4989
- */
4990
- public function undefine($scheme)
4991
- {
4992
- unset($this->schemes[$scheme]);
4993
- }
4994
-
4995
- /**
4996
- * {@inheritdoc}
4997
- */
4998
- public function create($parameters)
4999
- {
5000
- if (!$parameters instanceof ParametersInterface) {
5001
- $parameters = $this->createParameters($parameters);
5002
- }
5003
-
5004
- $scheme = $parameters->scheme;
5005
-
5006
- if (!isset($this->schemes[$scheme])) {
5007
- throw new \InvalidArgumentException("Unknown connection scheme: '$scheme'.");
5008
- }
5009
-
5010
- $initializer = $this->schemes[$scheme];
5011
-
5012
- if (is_callable($initializer)) {
5013
- $connection = call_user_func($initializer, $parameters, $this);
5014
- } else {
5015
- $connection = new $initializer($parameters);
5016
- $this->prepareConnection($connection);
5017
- }
5018
-
5019
- if (!$connection instanceof NodeConnectionInterface) {
5020
- throw new \UnexpectedValueException(
5021
- 'Objects returned by connection initializers must implement '.
5022
- "'Predis\Connection\NodeConnectionInterface'."
5023
- );
5024
- }
5025
-
5026
- return $connection;
5027
- }
5028
-
5029
- /**
5030
- * {@inheritdoc}
5031
- */
5032
- public function aggregate(AggregateConnectionInterface $connection, array $parameters)
5033
- {
5034
- foreach ($parameters as $node) {
5035
- $connection->add($node instanceof NodeConnectionInterface ? $node : $this->create($node));
5036
- }
5037
- }
5038
-
5039
- /**
5040
- * Assigns a default set of parameters applied to new connections.
5041
- *
5042
- * The set of parameters passed to create a new connection have precedence
5043
- * over the default values set for the connection factory.
5044
- *
5045
- * @param array $parameters Set of connection parameters.
5046
- */
5047
- public function setDefaultParameters(array $parameters)
5048
- {
5049
- $this->defaults = $parameters;
5050
- }
5051
-
5052
- /**
5053
- * Returns the default set of parameters applied to new connections.
5054
- *
5055
- * @return array
5056
- */
5057
- public function getDefaultParameters()
5058
- {
5059
- return $this->defaults;
5060
- }
5061
-
5062
- /**
5063
- * Creates a connection parameters instance from the supplied argument.
5064
- *
5065
- * @param mixed $parameters Original connection parameters.
5066
- *
5067
- * @return ParametersInterface
5068
- */
5069
- protected function createParameters($parameters)
5070
- {
5071
- if (is_string($parameters)) {
5072
- $parameters = Parameters::parse($parameters);
5073
- } else {
5074
- $parameters = $parameters ?: array();
5075
- }
5076
-
5077
- if ($this->defaults) {
5078
- $parameters += $this->defaults;
5079
- }
5080
-
5081
- return new Parameters($parameters);
5082
- }
5083
-
5084
- /**
5085
- * Prepares a connection instance after its initialization.
5086
- *
5087
- * @param NodeConnectionInterface $connection Connection instance.
5088
- */
5089
- protected function prepareConnection(NodeConnectionInterface $connection)
5090
- {
5091
- $parameters = $connection->getParameters();
5092
-
5093
- if (isset($parameters->password)) {
5094
- $connection->addConnectCommand(
5095
- new RawCommand(array('AUTH', $parameters->password))
5096
- );
5097
- }
5098
-
5099
- if (isset($parameters->database)) {
5100
- $connection->addConnectCommand(
5101
- new RawCommand(array('SELECT', $parameters->database))
5102
- );
5103
- }
5104
- }
5105
- }
5106
-
5107
- /**
5108
- * This class provides the implementation of a Predis connection that uses the
5109
- * PHP socket extension for network communication and wraps the phpiredis C
5110
- * extension (PHP bindings for hiredis) to parse the Redis protocol.
5111
- *
5112
- * This class is intended to provide an optional low-overhead alternative for
5113
- * processing responses from Redis compared to the standard pure-PHP classes.
5114
- * Differences in speed when dealing with short inline responses are practically
5115
- * nonexistent, the actual speed boost is for big multibulk responses when this
5116
- * protocol processor can parse and return responses very fast.
5117
- *
5118
- * For instructions on how to build and install the phpiredis extension, please
5119
- * consult the repository of the project.
5120
- *
5121
- * The connection parameters supported by this class are:
5122
- *
5123
- * - scheme: it can be either 'redis', 'tcp' or 'unix'.
5124
- * - host: hostname or IP address of the server.
5125
- * - port: TCP port of the server.
5126
- * - path: path of a UNIX domain socket when scheme is 'unix'.
5127
- * - timeout: timeout to perform the connection (default is 5 seconds).
5128
- * - read_write_timeout: timeout of read / write operations.
5129
- *
5130
- * @link http://github.com/nrk/phpiredis
5131
- *
5132
- * @author Daniele Alessandri <suppakilla@gmail.com>
5133
- */
5134
- class PhpiredisSocketConnection extends AbstractConnection
5135
- {
5136
- private $reader;
5137
-
5138
- /**
5139
- * {@inheritdoc}
5140
- */
5141
- public function __construct(ParametersInterface $parameters)
5142
- {
5143
- $this->assertExtensions();
5144
-
5145
- parent::__construct($parameters);
5146
-
5147
- $this->reader = $this->createReader();
5148
- }
5149
-
5150
- /**
5151
- * Disconnects from the server and destroys the underlying resource and the
5152
- * protocol reader resource when PHP's garbage collector kicks in.
5153
- */
5154
- public function __destruct()
5155
- {
5156
- phpiredis_reader_destroy($this->reader);
5157
-
5158
- parent::__destruct();
5159
- }
5160
-
5161
- /**
5162
- * Checks if the socket and phpiredis extensions are loaded in PHP.
5163
- */
5164
- protected function assertExtensions()
5165
- {
5166
- if (!extension_loaded('sockets')) {
5167
- throw new NotSupportedException(
5168
- 'The "sockets" extension is required by this connection backend.'
5169
- );
5170
- }
5171
-
5172
- if (!extension_loaded('phpiredis')) {
5173
- throw new NotSupportedException(
5174
- 'The "phpiredis" extension is required by this connection backend.'
5175
- );
5176
- }
5177
- }
5178
-
5179
- /**
5180
- * {@inheritdoc}
5181
- */
5182
- protected function assertParameters(ParametersInterface $parameters)
5183
- {
5184
- switch ($parameters->scheme) {
5185
- case 'tcp':
5186
- case 'redis':
5187
- case 'unix':
5188
- break;
5189
-
5190
- default:
5191
- throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
5192
- }
5193
-
5194
- if (isset($parameters->persistent)) {
5195
- throw new NotSupportedException(
5196
- 'Persistent connections are not supported by this connection backend.'
5197
- );
5198
- }
5199
-
5200
- return $parameters;
5201
- }
5202
-
5203
- /**
5204
- * Creates a new instance of the protocol reader resource.
5205
- *
5206
- * @return resource
5207
- */
5208
- private function createReader()
5209
- {
5210
- $reader = phpiredis_reader_create();
5211
-
5212
- phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
5213
- phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
5214
-
5215
- return $reader;
5216
- }
5217
-
5218
- /**
5219
- * Returns the underlying protocol reader resource.
5220
- *
5221
- * @return resource
5222
- */
5223
- protected function getReader()
5224
- {
5225
- return $this->reader;
5226
- }
5227
-
5228
- /**
5229
- * Returns the handler used by the protocol reader for inline responses.
5230
- *
5231
- * @return \Closure
5232
- */
5233
- protected function getStatusHandler()
5234
- {
5235
- static $statusHandler;
5236
-
5237
- if (!$statusHandler) {
5238
- $statusHandler = function ($payload) {
5239
- return StatusResponse::get($payload);
5240
- };
5241
- }
5242
-
5243
- return $statusHandler;
5244
- }
5245
-
5246
- /**
5247
- * Returns the handler used by the protocol reader for error responses.
5248
- *
5249
- * @return \Closure
5250
- */
5251
- protected function getErrorHandler()
5252
- {
5253
- static $errorHandler;
5254
-
5255
- if (!$errorHandler) {
5256
- $errorHandler = function ($errorMessage) {
5257
- return new ErrorResponse($errorMessage);
5258
- };
5259
- }
5260
-
5261
- return $errorHandler;
5262
- }
5263
-
5264
- /**
5265
- * Helper method used to throw exceptions on socket errors.
5266
- */
5267
- private function emitSocketError()
5268
- {
5269
- $errno = socket_last_error();
5270
- $errstr = socket_strerror($errno);
5271
-
5272
- $this->disconnect();
5273
-
5274
- $this->onConnectionError(trim($errstr), $errno);
5275
- }
5276
-
5277
- /**
5278
- * Gets the address of an host from connection parameters.
5279
- *
5280
- * @param ParametersInterface $parameters Parameters used to initialize the connection.
5281
- *
5282
- * @return string
5283
- */
5284
- protected static function getAddress(ParametersInterface $parameters)
5285
- {
5286
- if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) {
5287
- return $host;
5288
- }
5289
-
5290
- if ($host === $address = gethostbyname($host)) {
5291
- return false;
5292
- }
5293
-
5294
- return $address;
5295
- }
5296
-
5297
- /**
5298
- * {@inheritdoc}
5299
- */
5300
- protected function createResource()
5301
- {
5302
- $parameters = $this->parameters;
5303
-
5304
- if ($parameters->scheme === 'unix') {
5305
- $address = $parameters->path;
5306
- $domain = AF_UNIX;
5307
- $protocol = 0;
5308
- } else {
5309
- if (false === $address = self::getAddress($parameters)) {
5310
- $this->onConnectionError("Cannot resolve the address of '$parameters->host'.");
5311
- }
5312
-
5313
- $domain = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? AF_INET6 : AF_INET;
5314
- $protocol = SOL_TCP;
5315
- }
5316
-
5317
- $socket = @socket_create($domain, SOCK_STREAM, $protocol);
5318
-
5319
- if (!is_resource($socket)) {
5320
- $this->emitSocketError();
5321
- }
5322
-
5323
- $this->setSocketOptions($socket, $parameters);
5324
- $this->connectWithTimeout($socket, $address, $parameters);
5325
-
5326
- return $socket;
5327
- }
5328
-
5329
- /**
5330
- * Sets options on the socket resource from the connection parameters.
5331
- *
5332
- * @param resource $socket Socket resource.
5333
- * @param ParametersInterface $parameters Parameters used to initialize the connection.
5334
- */
5335
- private function setSocketOptions($socket, ParametersInterface $parameters)
5336
- {
5337
- if ($parameters->scheme !== 'unix') {
5338
- if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) {
5339
- $this->emitSocketError();
5340
- }
5341
-
5342
- if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
5343
- $this->emitSocketError();
5344
- }
5345
- }
5346
-
5347
- if (isset($parameters->read_write_timeout)) {
5348
- $rwtimeout = (float) $parameters->read_write_timeout;
5349
- $timeoutSec = floor($rwtimeout);
5350
- $timeoutUsec = ($rwtimeout - $timeoutSec) * 1000000;
5351
-
5352
- $timeout = array(
5353
- 'sec' => $timeoutSec,
5354
- 'usec' => $timeoutUsec,
5355
- );
5356
-
5357
- if (!socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)) {
5358
- $this->emitSocketError();
5359
- }
5360
-
5361
- if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)) {
5362
- $this->emitSocketError();
5363
- }
5364
- }
5365
- }
5366
-
5367
- /**
5368
- * Opens the actual connection to the server with a timeout.
5369
- *
5370
- * @param resource $socket Socket resource.
5371
- * @param string $address IP address (DNS-resolved from hostname)
5372
- * @param ParametersInterface $parameters Parameters used to initialize the connection.
5373
- *
5374
- * @return string
5375
- */
5376
- private function connectWithTimeout($socket, $address, ParametersInterface $parameters)
5377
- {
5378
- socket_set_nonblock($socket);
5379
-
5380
- if (@socket_connect($socket, $address, (int) $parameters->port) === false) {
5381
- $error = socket_last_error();
5382
-
5383
- if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) {
5384
- $this->emitSocketError();
5385
- }
5386
- }
5387
-
5388
- socket_set_block($socket);
5389
-
5390
- $null = null;
5391
- $selectable = array($socket);
5392
-
5393
- $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
5394
- $timeoutSecs = floor($timeout);
5395
- $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000;
5396
-
5397
- $selected = socket_select($selectable, $selectable, $null, $timeoutSecs, $timeoutUSecs);
5398
-
5399
- if ($selected === 2) {
5400
- $this->onConnectionError('Connection refused.', SOCKET_ECONNREFUSED);
5401
- }
5402
-
5403
- if ($selected === 0) {
5404
- $this->onConnectionError('Connection timed out.', SOCKET_ETIMEDOUT);
5405
- }
5406
-
5407
- if ($selected === false) {
5408
- $this->emitSocketError();
5409
- }
5410
- }
5411
-
5412
- /**
5413
- * {@inheritdoc}
5414
- */
5415
- public function connect()
5416
- {
5417
- if (parent::connect() && $this->initCommands) {
5418
- foreach ($this->initCommands as $command) {
5419
- $response = $this->executeCommand($command);
5420
-
5421
- if ($response instanceof ErrorResponseInterface) {
5422
- $this->onConnectionError("`{$command->getId()}` failed: $response", 0);
5423
- }
5424
- }
5425
- }
5426
- }
5427
-
5428
- /**
5429
- * {@inheritdoc}
5430
- */
5431
- public function disconnect()
5432
- {
5433
- if ($this->isConnected()) {
5434
- socket_close($this->getResource());
5435
- parent::disconnect();
5436
- }
5437
- }
5438
-
5439
- /**
5440
- * {@inheritdoc}
5441
- */
5442
- protected function write($buffer)
5443
- {
5444
- $socket = $this->getResource();
5445
-
5446
- while (($length = strlen($buffer)) > 0) {
5447
- $written = socket_write($socket, $buffer, $length);
5448
-
5449
- if ($length === $written) {
5450
- return;
5451
- }
5452
-
5453
- if ($written === false) {
5454
- $this->onConnectionError('Error while writing bytes to the server.');
5455
- }
5456
-
5457
- $buffer = substr($buffer, $written);
5458
- }
5459
- }
5460
-
5461
- /**
5462
- * {@inheritdoc}
5463
- */
5464
- public function read()
5465
- {
5466
- $socket = $this->getResource();
5467
- $reader = $this->reader;
5468
-
5469
- while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
5470
- if (@socket_recv($socket, $buffer, 4096, 0) === false || $buffer === '' || $buffer === null) {
5471
- $this->emitSocketError();
5472
- }
5473
-
5474
- phpiredis_reader_feed($reader, $buffer);
5475
- }
5476
-
5477
- if ($state === PHPIREDIS_READER_STATE_COMPLETE) {
5478
- return phpiredis_reader_get_reply($reader);
5479
- } else {
5480
- $this->onProtocolError(phpiredis_reader_get_error($reader));
5481
-
5482
- return;
5483
- }
5484
- }
5485
-
5486
- /**
5487
- * {@inheritdoc}
5488
- */
5489
- public function writeRequest(CommandInterface $command)
5490
- {
5491
- $arguments = $command->getArguments();
5492
- array_unshift($arguments, $command->getId());
5493
-
5494
- $this->write(phpiredis_format_command($arguments));
5495
- }
5496
-
5497
- /**
5498
- * {@inheritdoc}
5499
- */
5500
- public function __wakeup()
5501
- {
5502
- $this->assertExtensions();
5503
- $this->reader = $this->createReader();
5504
- }
5505
- }
5506
-
5507
- /**
5508
- * This class provides the implementation of a Predis connection that uses PHP's
5509
- * streams for network communication and wraps the phpiredis C extension (PHP
5510
- * bindings for hiredis) to parse and serialize the Redis protocol.
5511
- *
5512
- * This class is intended to provide an optional low-overhead alternative for
5513
- * processing responses from Redis compared to the standard pure-PHP classes.
5514
- * Differences in speed when dealing with short inline responses are practically
5515
- * nonexistent, the actual speed boost is for big multibulk responses when this
5516
- * protocol processor can parse and return responses very fast.
5517
- *
5518
- * For instructions on how to build and install the phpiredis extension, please
5519
- * consult the repository of the project.
5520
- *
5521
- * The connection parameters supported by this class are:
5522
- *
5523
- * - scheme: it can be either 'redis', 'tcp' or 'unix'.
5524
- * - host: hostname or IP address of the server.
5525
- * - port: TCP port of the server.
5526
- * - path: path of a UNIX domain socket when scheme is 'unix'.
5527
- * - timeout: timeout to perform the connection.
5528
- * - read_write_timeout: timeout of read / write operations.
5529
- * - async_connect: performs the connection asynchronously.
5530
- * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing.
5531
- * - persistent: the connection is left intact after a GC collection.
5532
- *
5533
- * @link https://github.com/nrk/phpiredis
5534
- *
5535
- * @author Daniele Alessandri <suppakilla@gmail.com>
5536
- */
5537
- class PhpiredisStreamConnection extends StreamConnection
5538
- {
5539
- private $reader;
5540
-
5541
- /**
5542
- * {@inheritdoc}
5543
- */
5544
- public function __construct(ParametersInterface $parameters)
5545
- {
5546
- $this->assertExtensions();
5547
-
5548
- parent::__construct($parameters);
5549
-
5550
- $this->reader = $this->createReader();
5551
- }
5552
-
5553
- /**
5554
- * {@inheritdoc}
5555
- */
5556
- public function __destruct()
5557
- {
5558
- phpiredis_reader_destroy($this->reader);
5559
-
5560
- parent::__destruct();
5561
- }
5562
-
5563
- /**
5564
- * Checks if the phpiredis extension is loaded in PHP.
5565
- */
5566
- private function assertExtensions()
5567
- {
5568
- if (!extension_loaded('phpiredis')) {
5569
- throw new NotSupportedException(
5570
- 'The "phpiredis" extension is required by this connection backend.'
5571
- );
5572
- }
5573
- }
5574
-
5575
- /**
5576
- * {@inheritdoc}
5577
- */
5578
- protected function assertSslSupport(ParametersInterface $parameters)
5579
- {
5580
- throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.');
5581
- }
5582
-
5583
- /**
5584
- * {@inheritdoc}
5585
- */
5586
- protected function createStreamSocket(ParametersInterface $parameters, $address, $flags, $context = null)
5587
- {
5588
- $socket = null;
5589
- $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
5590
-
5591
- $resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags);
5592
-
5593
- if (!$resource) {
5594
- $this->onConnectionError(trim($errstr), $errno);
5595
- }
5596
-
5597
- if (isset($parameters->read_write_timeout) && function_exists('socket_import_stream')) {
5598
- $rwtimeout = (float) $parameters->read_write_timeout;
5599
- $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1;
5600
-
5601
- $timeout = array(
5602
- 'sec' => $timeoutSeconds = floor($rwtimeout),
5603
- 'usec' => ($rwtimeout - $timeoutSeconds) * 1000000,
5604
- );
5605
-
5606
- $socket = $socket ?: socket_import_stream($resource);
5607
- @socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout);
5608
- @socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
5609
- }
5610
-
5611
- if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) {
5612
- $socket = $socket ?: socket_import_stream($resource);
5613
- socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
5614
- }
5615
-
5616
- return $resource;
5617
- }
5618
-
5619
- /**
5620
- * Creates a new instance of the protocol reader resource.
5621
- *
5622
- * @return resource
5623
- */
5624
- private function createReader()
5625
- {
5626
- $reader = phpiredis_reader_create();
5627
-
5628
- phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
5629
- phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
5630
-
5631
- return $reader;
5632
- }
5633
-
5634
- /**
5635
- * Returns the underlying protocol reader resource.
5636
- *
5637
- * @return resource
5638
- */
5639
- protected function getReader()
5640
- {
5641
- return $this->reader;
5642
- }
5643
-
5644
- /**
5645
- * Returns the handler used by the protocol reader for inline responses.
5646
- *
5647
- * @return \Closure
5648
- */
5649
- protected function getStatusHandler()
5650
- {
5651
- static $statusHandler;
5652
-
5653
- if (!$statusHandler) {
5654
- $statusHandler = function ($payload) {
5655
- return StatusResponse::get($payload);
5656
- };
5657
- }
5658
-
5659
- return $statusHandler;
5660
- }
5661
-
5662
- /**
5663
- * Returns the handler used by the protocol reader for error responses.
5664
- *
5665
- * @return \Closure
5666
- */
5667
- protected function getErrorHandler()
5668
- {
5669
- static $errorHandler;
5670
-
5671
- if (!$errorHandler) {
5672
- $errorHandler = function ($errorMessage) {
5673
- return new ErrorResponse($errorMessage);
5674
- };
5675
- }
5676
-
5677
- return $errorHandler;
5678
- }
5679
-
5680
- /**
5681
- * {@inheritdoc}
5682
- */
5683
- public function read()
5684
- {
5685
- $socket = $this->getResource();
5686
- $reader = $this->reader;
5687
-
5688
- while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
5689
- $buffer = stream_socket_recvfrom($socket, 4096);
5690
-
5691
- if ($buffer === false || $buffer === '') {
5692
- $this->onConnectionError('Error while reading bytes from the server.');
5693
- }
5694
-
5695
- phpiredis_reader_feed($reader, $buffer);
5696
- }
5697
-
5698
- if ($state === PHPIREDIS_READER_STATE_COMPLETE) {
5699
- return phpiredis_reader_get_reply($reader);
5700
- } else {
5701
- $this->onProtocolError(phpiredis_reader_get_error($reader));
5702
-
5703
- return;
5704
- }
5705
- }
5706
-
5707
- /**
5708
- * {@inheritdoc}
5709
- */
5710
- public function writeRequest(CommandInterface $command)
5711
- {
5712
- $arguments = $command->getArguments();
5713
- array_unshift($arguments, $command->getId());
5714
-
5715
- $this->write(phpiredis_format_command($arguments));
5716
- }
5717
-
5718
- /**
5719
- * {@inheritdoc}
5720
- */
5721
- public function __wakeup()
5722
- {
5723
- $this->assertExtensions();
5724
- $this->reader = $this->createReader();
5725
- }
5726
- }
5727
-
5728
- /**
5729
- * Exception class that identifies connection-related errors.
5730
- *
5731
- * @author Daniele Alessandri <suppakilla@gmail.com>
5732
- */
5733
- class ConnectionException extends CommunicationException
5734
- {
5735
- }
5736
-
5737
- /**
5738
- * Connection abstraction to Redis servers based on PHP's stream that uses an
5739
- * external protocol processor defining the protocol used for the communication.
5740
- *
5741
- * @author Daniele Alessandri <suppakilla@gmail.com>
5742
- */
5743
- class CompositeStreamConnection extends StreamConnection implements CompositeConnectionInterface
5744
- {
5745
- protected $protocol;
5746
-
5747
- /**
5748
- * @param ParametersInterface $parameters Initialization parameters for the connection.
5749
- * @param ProtocolProcessorInterface $protocol Protocol processor.
5750
- */
5751
- public function __construct(
5752
- ParametersInterface $parameters,
5753
- ProtocolProcessorInterface $protocol = null
5754
- ) {
5755
- $this->parameters = $this->assertParameters($parameters);
5756
- $this->protocol = $protocol ?: new TextProtocolProcessor();
5757
- }
5758
-
5759
- /**
5760
- * {@inheritdoc}
5761
- */
5762
- public function getProtocol()
5763
- {
5764
- return $this->protocol;
5765
- }
5766
-
5767
- /**
5768
- * {@inheritdoc}
5769
- */
5770
- public function writeBuffer($buffer)
5771
- {
5772
- $this->write($buffer);
5773
- }
5774
-
5775
- /**
5776
- * {@inheritdoc}
5777
- */
5778
- public function readBuffer($length)
5779
- {
5780
- if ($length <= 0) {
5781
- throw new \InvalidArgumentException('Length parameter must be greater than 0.');
5782
- }
5783
-
5784
- $value = '';
5785
- $socket = $this->getResource();
5786
-
5787
- do {
5788
- $chunk = fread($socket, $length);
5789
-
5790
- if ($chunk === false || $chunk === '') {
5791
- $this->onConnectionError('Error while reading bytes from the server.');
5792
- }
5793
-
5794
- $value .= $chunk;
5795
- } while (($length -= strlen($chunk)) > 0);
5796
-
5797
- return $value;
5798
- }
5799
-
5800
- /**
5801
- * {@inheritdoc}
5802
- */
5803
- public function readLine()
5804
- {
5805
- $value = '';
5806
- $socket = $this->getResource();
5807
-
5808
- do {
5809
- $chunk = fgets($socket);
5810
-
5811
- if ($chunk === false || $chunk === '') {
5812
- $this->onConnectionError('Error while reading line from the server.');
5813
- }
5814
-
5815
- $value .= $chunk;
5816
- } while (substr($value, -2) !== "\r\n");
5817
-
5818
- return substr($value, 0, -2);
5819
- }
5820
-
5821
- /**
5822
- * {@inheritdoc}
5823
- */
5824
- public function writeRequest(CommandInterface $command)
5825
- {
5826
- $this->protocol->write($this, $command);
5827
- }
5828
-
5829
- /**
5830
- * {@inheritdoc}
5831
- */
5832
- public function read()
5833
- {
5834
- return $this->protocol->read($this);
5835
- }
5836
-
5837
- /**
5838
- * {@inheritdoc}
5839
- */
5840
- public function __sleep()
5841
- {
5842
- return array_merge(parent::__sleep(), array('protocol'));
5843
- }
5844
- }
5845
-
5846
- /**
5847
- * Container for connection parameters used to initialize connections to Redis.
5848
- *
5849
- * {@inheritdoc}
5850
- *
5851
- * @author Daniele Alessandri <suppakilla@gmail.com>
5852
- */
5853
- class Parameters implements ParametersInterface
5854
- {
5855
- private $parameters;
5856
-
5857
- private static $defaults = array(
5858
- 'scheme' => 'tcp',
5859
- 'host' => '127.0.0.1',
5860
- 'port' => 6379,
5861
- );
5862
-
5863
- /**
5864
- * @param array $parameters Named array of connection parameters.
5865
- */
5866
- public function __construct(array $parameters = array())
5867
- {
5868
- $this->parameters = $this->filter($parameters) + $this->getDefaults();
5869
- }
5870
-
5871
- /**
5872
- * Returns some default parameters with their values.
5873
- *
5874
- * @return array
5875
- */
5876
- protected function getDefaults()
5877
- {
5878
- return self::$defaults;
5879
- }
5880
-
5881
- /**
5882
- * Creates a new instance by supplying the initial parameters either in the
5883
- * form of an URI string or a named array.
5884
- *
5885
- * @param array|string $parameters Set of connection parameters.
5886
- *
5887
- * @return Parameters
5888
- */
5889
- public static function create($parameters)
5890
- {
5891
- if (is_string($parameters)) {
5892
- $parameters = static::parse($parameters);
5893
- }
5894
-
5895
- return new static($parameters ?: array());
5896
- }
5897
-
5898
- /**
5899
- * Parses an URI string returning an array of connection parameters.
5900
- *
5901
- * When using the "redis" and "rediss" schemes the URI is parsed according
5902
- * to the rules defined by the provisional registration documents approved
5903
- * by IANA. If the URI has a password in its "user-information" part or a
5904
- * database number in the "path" part these values override the values of
5905
- * "password" and "database" if they are present in the "query" part.
5906
- *
5907
- * @link http://www.iana.org/assignments/uri-schemes/prov/redis
5908
- * @link http://www.iana.org/assignments/uri-schemes/prov/rediss
5909
- *
5910
- * @param string $uri URI string.
5911
- *
5912
- * @throws \InvalidArgumentException
5913
- *
5914
- * @return array
5915
- */
5916
- public static function parse($uri)
5917
- {
5918
- if (stripos($uri, 'unix://') === 0) {
5919
- // parse_url() can parse unix:/path/to/sock so we do not need the
5920
- // unix:///path/to/sock hack, we will support it anyway until 2.0.
5921
- $uri = str_ireplace('unix://', 'unix:', $uri);
5922
- }
5923
-
5924
- if (!$parsed = parse_url($uri)) {
5925
- throw new \InvalidArgumentException("Invalid parameters URI: $uri");
5926
- }
5927
-
5928
- if (
5929
- isset($parsed['host'])
5930
- && false !== strpos($parsed['host'], '[')
5931
- && false !== strpos($parsed['host'], ']')
5932
- ) {
5933
- $parsed['host'] = substr($parsed['host'], 1, -1);
5934
- }
5935
-
5936
- if (isset($parsed['query'])) {
5937
- parse_str($parsed['query'], $queryarray);
5938
- unset($parsed['query']);
5939
-
5940
- $parsed = array_merge($parsed, $queryarray);
5941
- }
5942
-
5943
- if (stripos($uri, 'redis') === 0) {
5944
- if (isset($parsed['pass'])) {
5945
- $parsed['password'] = $parsed['pass'];
5946
- unset($parsed['pass']);
5947
- }
5948
-
5949
- if (isset($parsed['path']) && preg_match('/^\/(\d+)(\/.*)?/', $parsed['path'], $path)) {
5950
- $parsed['database'] = $path[1];
5951
-
5952
- if (isset($path[2])) {
5953
- $parsed['path'] = $path[2];
5954
- } else {
5955
- unset($parsed['path']);
5956
- }
5957
- }
5958
- }
5959
-
5960
- return $parsed;
5961
- }
5962
-
5963
- /**
5964
- * Validates and converts each value of the connection parameters array.
5965
- *
5966
- * @param array $parameters Connection parameters.
5967
- *
5968
- * @return array
5969
- */
5970
- protected function filter(array $parameters)
5971
- {
5972
- return $parameters ?: array();
5973
- }
5974
-
5975
- /**
5976
- * {@inheritdoc}
5977
- */
5978
- public function __get($parameter)
5979
- {
5980
- if (isset($this->parameters[$parameter])) {
5981
- return $this->parameters[$parameter];
5982
- }
5983
- }
5984
-
5985
- /**
5986
- * {@inheritdoc}
5987
- */
5988
- public function __isset($parameter)
5989
- {
5990
- return isset($this->parameters[$parameter]);
5991
- }
5992
-
5993
- /**
5994
- * {@inheritdoc}
5995
- */
5996
- public function toArray()
5997
- {
5998
- return $this->parameters;
5999
- }
6000
-
6001
- /**
6002
- * {@inheritdoc}
6003
- */
6004
- public function __sleep()
6005
- {
6006
- return array('parameters');
6007
- }
6008
- }
6009
-
6010
- /**
6011
- * This class implements a Predis connection that actually talks with Webdis
6012
- * instead of connecting directly to Redis. It relies on the cURL extension to
6013
- * communicate with the web server and the phpiredis extension to parse the
6014
- * protocol for responses returned in the http response bodies.
6015
- *
6016
- * Some features are not yet available or they simply cannot be implemented:
6017
- * - Pipelining commands.
6018
- * - Publish / Subscribe.
6019
- * - MULTI / EXEC transactions (not yet supported by Webdis).
6020
- *
6021
- * The connection parameters supported by this class are:
6022
- *
6023
- * - scheme: must be 'http'.
6024
- * - host: hostname or IP address of the server.
6025
- * - port: TCP port of the server.
6026
- * - timeout: timeout to perform the connection (default is 5 seconds).
6027
- * - user: username for authentication.
6028
- * - pass: password for authentication.
6029
- *
6030
- * @link http://webd.is
6031
- * @link http://github.com/nicolasff/webdis
6032
- * @link http://github.com/seppo0010/phpiredis
6033
- *
6034
- * @author Daniele Alessandri <suppakilla@gmail.com>
6035
- */
6036
- class WebdisConnection implements NodeConnectionInterface
6037
- {
6038
- private $parameters;
6039
- private $resource;
6040
- private $reader;
6041
-
6042
- /**
6043
- * @param ParametersInterface $parameters Initialization parameters for the connection.
6044
- *
6045
- * @throws \InvalidArgumentException
6046
- */
6047
- public function __construct(ParametersInterface $parameters)
6048
- {
6049
- $this->assertExtensions();
6050
-
6051
- if ($parameters->scheme !== 'http') {
6052
- throw new \InvalidArgumentException("Invalid scheme: '{$parameters->scheme}'.");
6053
- }
6054
-
6055
- $this->parameters = $parameters;
6056
-
6057
- $this->resource = $this->createCurl();
6058
- $this->reader = $this->createReader();
6059
- }
6060
-
6061
- /**
6062
- * Frees the underlying cURL and protocol reader resources when the garbage
6063
- * collector kicks in.
6064
- */
6065
- public function __destruct()
6066
- {
6067
- curl_close($this->resource);
6068
- phpiredis_reader_destroy($this->reader);
6069
- }
6070
-
6071
- /**
6072
- * Helper method used to throw on unsupported methods.
6073
- *
6074
- * @param string $method Name of the unsupported method.
6075
- *
6076
- * @throws NotSupportedException
6077
- */
6078
- private function throwNotSupportedException($method)
6079
- {
6080
- $class = __CLASS__;
6081
- throw new NotSupportedException("The method $class::$method() is not supported.");
6082
- }
6083
-
6084
- /**
6085
- * Checks if the cURL and phpiredis extensions are loaded in PHP.
6086
- */
6087
- private function assertExtensions()
6088
- {
6089
- if (!extension_loaded('curl')) {
6090
- throw new NotSupportedException(
6091
- 'The "curl" extension is required by this connection backend.'
6092
- );
6093
- }
6094
-
6095
- if (!extension_loaded('phpiredis')) {
6096
- throw new NotSupportedException(
6097
- 'The "phpiredis" extension is required by this connection backend.'
6098
- );
6099
- }
6100
- }
6101
-
6102
- /**
6103
- * Initializes cURL.
6104
- *
6105
- * @return resource
6106
- */
6107
- private function createCurl()
6108
- {
6109
- $parameters = $this->getParameters();
6110
- $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000;
6111
-
6112
- if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) {
6113
- $host = "[$host]";
6114
- }
6115
-
6116
- $options = array(
6117
- CURLOPT_FAILONERROR => true,
6118
- CURLOPT_CONNECTTIMEOUT_MS => $timeout,
6119
- CURLOPT_URL => "$parameters->scheme://$host:$parameters->port",
6120
- CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
6121
- CURLOPT_POST => true,
6122
- CURLOPT_WRITEFUNCTION => array($this, 'feedReader'),
6123
- );
6124
-
6125
- if (isset($parameters->user, $parameters->pass)) {
6126
- $options[CURLOPT_USERPWD] = "{$parameters->user}:{$parameters->pass}";
6127
- }
6128
-
6129
- curl_setopt_array($resource = curl_init(), $options);
6130
-
6131
- return $resource;
6132
- }
6133
-
6134
- /**
6135
- * Initializes the phpiredis protocol reader.
6136
- *
6137
- * @return resource
6138
- */
6139
- private function createReader()
6140
- {
6141
- $reader = phpiredis_reader_create();
6142
-
6143
- phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
6144
- phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
6145
-
6146
- return $reader;
6147
- }
6148
-
6149
- /**
6150
- * Returns the handler used by the protocol reader for inline responses.
6151
- *
6152
- * @return \Closure
6153
- */
6154
- protected function getStatusHandler()
6155
- {
6156
- static $statusHandler;
6157
-
6158
- if (!$statusHandler) {
6159
- $statusHandler = function ($payload) {
6160
- return StatusResponse::get($payload);
6161
- };
6162
- }
6163
-
6164
- return $statusHandler;
6165
- }
6166
-
6167
- /**
6168
- * Returns the handler used by the protocol reader for error responses.
6169
- *
6170
- * @return \Closure
6171
- */
6172
- protected function getErrorHandler()
6173
- {
6174
- static $errorHandler;
6175
-
6176
- if (!$errorHandler) {
6177
- $errorHandler = function ($errorMessage) {
6178
- return new ErrorResponse($errorMessage);
6179
- };
6180
- }
6181
-
6182
- return $errorHandler;
6183
- }
6184
-
6185
- /**
6186
- * Feeds the phpredis reader resource with the data read from the network.
6187
- *
6188
- * @param resource $resource Reader resource.
6189
- * @param string $buffer Buffer of data read from a connection.
6190
- *
6191
- * @return int
6192
- */
6193
- protected function feedReader($resource, $buffer)
6194
- {
6195
- phpiredis_reader_feed($this->reader, $buffer);
6196
-
6197
- return strlen($buffer);
6198
- }
6199
-
6200
- /**
6201
- * {@inheritdoc}
6202
- */
6203
- public function connect()
6204
- {
6205
- // NOOP
6206
- }
6207
-
6208
- /**
6209
- * {@inheritdoc}
6210
- */
6211
- public function disconnect()
6212
- {
6213
- // NOOP
6214
- }
6215
-
6216
- /**
6217
- * {@inheritdoc}
6218
- */
6219
- public function isConnected()
6220
- {
6221
- return true;
6222
- }
6223
-
6224
- /**
6225
- * Checks if the specified command is supported by this connection class.
6226
- *
6227
- * @param CommandInterface $command Command instance.
6228
- *
6229
- * @throws NotSupportedException
6230
- *
6231
- * @return string
6232
- */
6233
- protected function getCommandId(CommandInterface $command)
6234
- {
6235
- switch ($commandID = $command->getId()) {
6236
- case 'AUTH':
6237
- case 'SELECT':
6238
- case 'MULTI':
6239
- case 'EXEC':
6240
- case 'WATCH':
6241
- case 'UNWATCH':
6242
- case 'DISCARD':
6243
- case 'MONITOR':
6244
- throw new NotSupportedException("Command '$commandID' is not allowed by Webdis.");
6245
-
6246
- default:
6247
- return $commandID;
6248
- }
6249
- }
6250
-
6251
- /**
6252
- * {@inheritdoc}
6253
- */
6254
- public function writeRequest(CommandInterface $command)
6255
- {
6256
- $this->throwNotSupportedException(__FUNCTION__);
6257
- }
6258
-
6259
- /**
6260
- * {@inheritdoc}
6261
- */
6262
- public function readResponse(CommandInterface $command)
6263
- {
6264
- $this->throwNotSupportedException(__FUNCTION__);
6265
- }
6266
-
6267
- /**
6268
- * {@inheritdoc}
6269
- */
6270
- public function executeCommand(CommandInterface $command)
6271
- {
6272
- $resource = $this->resource;
6273
- $commandId = $this->getCommandId($command);
6274
-
6275
- if ($arguments = $command->getArguments()) {
6276
- $arguments = implode('/', array_map('urlencode', $arguments));
6277
- $serializedCommand = "$commandId/$arguments.raw";
6278
- } else {
6279
- $serializedCommand = "$commandId.raw";
6280
- }
6281
-
6282
- curl_setopt($resource, CURLOPT_POSTFIELDS, $serializedCommand);
6283
-
6284
- if (curl_exec($resource) === false) {
6285
- $error = curl_error($resource);
6286
- $errno = curl_errno($resource);
6287
-
6288
- throw new ConnectionException($this, trim($error), $errno);
6289
- }
6290
-
6291
- if (phpiredis_reader_get_state($this->reader) !== PHPIREDIS_READER_STATE_COMPLETE) {
6292
- throw new ProtocolException($this, phpiredis_reader_get_error($this->reader));
6293
- }
6294
-
6295
- return phpiredis_reader_get_reply($this->reader);
6296
- }
6297
-
6298
- /**
6299
- * {@inheritdoc}
6300
- */
6301
- public function getResource()
6302
- {
6303
- return $this->resource;
6304
- }
6305
-
6306
- /**
6307
- * {@inheritdoc}
6308
- */
6309
- public function getParameters()
6310
- {
6311
- return $this->parameters;
6312
- }
6313
-
6314
- /**
6315
- * {@inheritdoc}
6316
- */
6317
- public function addConnectCommand(CommandInterface $command)
6318
- {
6319
- $this->throwNotSupportedException(__FUNCTION__);
6320
- }
6321
-
6322
- /**
6323
- * {@inheritdoc}
6324
- */
6325
- public function read()
6326
- {
6327
- $this->throwNotSupportedException(__FUNCTION__);
6328
- }
6329
-
6330
- /**
6331
- * {@inheritdoc}
6332
- */
6333
- public function __toString()
6334
- {
6335
- return "{$this->parameters->host}:{$this->parameters->port}";
6336
- }
6337
-
6338
- /**
6339
- * {@inheritdoc}
6340
- */
6341
- public function __sleep()
6342
- {
6343
- return array('parameters');
6344
- }
6345
-
6346
- /**
6347
- * {@inheritdoc}
6348
- */
6349
- public function __wakeup()
6350
- {
6351
- $this->assertExtensions();
6352
-
6353
- $this->resource = $this->createCurl();
6354
- $this->reader = $this->createReader();
6355
- }
6356
- }
6357
-
6358
- /* --------------------------------------------------------------------------- */
6359
-
6360
- namespace Predis\Profile;
6361
-
6362
- use Predis\ClientException;
6363
- use Predis\Command\Processor\ProcessorInterface;
6364
- use Predis\Command\CommandInterface;
6365
-
6366
- /**
6367
- * A profile defines all the features and commands supported by certain versions
6368
- * of Redis. Instances of Predis\Client should use a server profile matching the
6369
- * version of Redis being used.
6370
- *
6371
- * @author Daniele Alessandri <suppakilla@gmail.com>
6372
- */
6373
- interface ProfileInterface
6374
- {
6375
- /**
6376
- * Returns the profile version corresponding to the Redis version.
6377
- *
6378
- * @return string
6379
- */
6380
- public function getVersion();
6381
-
6382
- /**
6383
- * Checks if the profile supports the specified command.
6384
- *
6385
- * @param string $commandID Command ID.
6386
- *
6387
- * @return bool
6388
- */
6389
- public function supportsCommand($commandID);
6390
-
6391
- /**
6392
- * Checks if the profile supports the specified list of commands.
6393
- *
6394
- * @param array $commandIDs List of command IDs.
6395
- *
6396
- * @return string
6397
- */
6398
- public function supportsCommands(array $commandIDs);
6399
-
6400
- /**
6401
- * Creates a new command instance.
6402
- *
6403
- * @param string $commandID Command ID.
6404
- * @param array $arguments Arguments for the command.
6405
- *
6406
- * @return CommandInterface
6407
- */
6408
- public function createCommand($commandID, array $arguments = array());
6409
- }
6410
-
6411
- /**
6412
- * Base class implementing common functionalities for Redis server profiles.
6413
- *
6414
- * @author Daniele Alessandri <suppakilla@gmail.com>
6415
- */
6416
- abstract class RedisProfile implements ProfileInterface
6417
- {
6418
- private $commands;
6419
- private $processor;
6420
-
6421
- /**
6422
- *
6423
- */
6424
- public function __construct()
6425
- {
6426
- $this->commands = $this->getSupportedCommands();
6427
- }
6428
-
6429
- /**
6430
- * Returns a map of all the commands supported by the profile and their
6431
- * actual PHP classes.
6432
- *
6433
- * @return array
6434
- */
6435
- abstract protected function getSupportedCommands();
6436
-
6437
- /**
6438
- * {@inheritdoc}
6439
- */
6440
- public function supportsCommand($commandID)
6441
- {
6442
- return isset($this->commands[strtoupper($commandID)]);
6443
- }
6444
-
6445
- /**
6446
- * {@inheritdoc}
6447
- */
6448
- public function supportsCommands(array $commandIDs)
6449
- {
6450
- foreach ($commandIDs as $commandID) {
6451
- if (!$this->supportsCommand($commandID)) {
6452
- return false;
6453
- }
6454
- }
6455
-
6456
- return true;
6457
- }
6458
-
6459
- /**
6460
- * Returns the fully-qualified name of a class representing the specified
6461
- * command ID registered in the current server profile.
6462
- *
6463
- * @param string $commandID Command ID.
6464
- *
6465
- * @return string|null
6466
- */
6467
- public function getCommandClass($commandID)
6468
- {
6469
- if (isset($this->commands[$commandID = strtoupper($commandID)])) {
6470
- return $this->commands[$commandID];
6471
- }
6472
- }
6473
-
6474
- /**
6475
- * {@inheritdoc}
6476
- */
6477
- public function createCommand($commandID, array $arguments = array())
6478
- {
6479
- $commandID = strtoupper($commandID);
6480
-
6481
- if (!isset($this->commands[$commandID])) {
6482
- throw new ClientException("Command '$commandID' is not a registered Redis command.");
6483
- }
6484
-
6485
- $commandClass = $this->commands[$commandID];
6486
- $command = new $commandClass();
6487
- $command->setArguments($arguments);
6488
-
6489
- if (isset($this->processor)) {
6490
- $this->processor->process($command);
6491
- }
6492
-
6493
- return $command;
6494
- }
6495
-
6496
- /**
6497
- * Defines a new command in the server profile.
6498
- *
6499
- * @param string $commandID Command ID.
6500
- * @param string $class Fully-qualified name of a Predis\Command\CommandInterface.
6501
- *
6502
- * @throws \InvalidArgumentException
6503
- */
6504
- public function defineCommand($commandID, $class)
6505
- {
6506
- $reflection = new \ReflectionClass($class);
6507
-
6508
- if (!$reflection->isSubclassOf('Predis\Command\CommandInterface')) {
6509
- throw new \InvalidArgumentException("The class '$class' is not a valid command class.");
6510
- }
6511
-
6512
- $this->commands[strtoupper($commandID)] = $class;
6513
- }
6514
-
6515
- /**
6516
- * {@inheritdoc}
6517
- */
6518
- public function setProcessor(ProcessorInterface $processor = null)
6519
- {
6520
- $this->processor = $processor;
6521
- }
6522
-
6523
- /**
6524
- * {@inheritdoc}
6525
- */
6526
- public function getProcessor()
6527
- {
6528
- return $this->processor;
6529
- }
6530
-
6531
- /**
6532
- * Returns the version of server profile as its string representation.
6533
- *
6534
- * @return string
6535
- */
6536
- public function __toString()
6537
- {
6538
- return $this->getVersion();
6539
- }
6540
- }
6541
-
6542
- /**
6543
- * Server profile for Redis 3.0.
6544
- *
6545
- * @author Daniele Alessandri <suppakilla@gmail.com>
6546
- */
6547
- class RedisVersion320 extends RedisProfile
6548
- {
6549
- /**
6550
- * {@inheritdoc}
6551
- */
6552
- public function getVersion()
6553
- {
6554
- return '3.2';
6555
- }
6556
-
6557
- /**
6558
- * {@inheritdoc}
6559
- */
6560
- public function getSupportedCommands()
6561
- {
6562
- return array(
6563
- /* ---------------- Redis 1.2 ---------------- */
6564
-
6565
- /* commands operating on the key space */
6566
- 'EXISTS' => 'Predis\Command\KeyExists',
6567
- 'DEL' => 'Predis\Command\KeyDelete',
6568
- 'TYPE' => 'Predis\Command\KeyType',
6569
- 'KEYS' => 'Predis\Command\KeyKeys',
6570
- 'RANDOMKEY' => 'Predis\Command\KeyRandom',
6571
- 'RENAME' => 'Predis\Command\KeyRename',
6572
- 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
6573
- 'EXPIRE' => 'Predis\Command\KeyExpire',
6574
- 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
6575
- 'TTL' => 'Predis\Command\KeyTimeToLive',
6576
- 'MOVE' => 'Predis\Command\KeyMove',
6577
- 'SORT' => 'Predis\Command\KeySort',
6578
- 'DUMP' => 'Predis\Command\KeyDump',
6579
- 'RESTORE' => 'Predis\Command\KeyRestore',
6580
-
6581
- /* commands operating on string values */
6582
- 'SET' => 'Predis\Command\StringSet',
6583
- 'SETNX' => 'Predis\Command\StringSetPreserve',
6584
- 'MSET' => 'Predis\Command\StringSetMultiple',
6585
- 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
6586
- 'GET' => 'Predis\Command\StringGet',
6587
- 'MGET' => 'Predis\Command\StringGetMultiple',
6588
- 'GETSET' => 'Predis\Command\StringGetSet',
6589
- 'INCR' => 'Predis\Command\StringIncrement',
6590
- 'INCRBY' => 'Predis\Command\StringIncrementBy',
6591
- 'DECR' => 'Predis\Command\StringDecrement',
6592
- 'DECRBY' => 'Predis\Command\StringDecrementBy',
6593
-
6594
- /* commands operating on lists */
6595
- 'RPUSH' => 'Predis\Command\ListPushTail',
6596
- 'LPUSH' => 'Predis\Command\ListPushHead',
6597
- 'LLEN' => 'Predis\Command\ListLength',
6598
- 'LRANGE' => 'Predis\Command\ListRange',
6599
- 'LTRIM' => 'Predis\Command\ListTrim',
6600
- 'LINDEX' => 'Predis\Command\ListIndex',
6601
- 'LSET' => 'Predis\Command\ListSet',
6602
- 'LREM' => 'Predis\Command\ListRemove',
6603
- 'LPOP' => 'Predis\Command\ListPopFirst',
6604
- 'RPOP' => 'Predis\Command\ListPopLast',
6605
- 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
6606
-
6607
- /* commands operating on sets */
6608
- 'SADD' => 'Predis\Command\SetAdd',
6609
- 'SREM' => 'Predis\Command\SetRemove',
6610
- 'SPOP' => 'Predis\Command\SetPop',
6611
- 'SMOVE' => 'Predis\Command\SetMove',
6612
- 'SCARD' => 'Predis\Command\SetCardinality',
6613
- 'SISMEMBER' => 'Predis\Command\SetIsMember',
6614
- 'SINTER' => 'Predis\Command\SetIntersection',
6615
- 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
6616
- 'SUNION' => 'Predis\Command\SetUnion',
6617
- 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
6618
- 'SDIFF' => 'Predis\Command\SetDifference',
6619
- 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
6620
- 'SMEMBERS' => 'Predis\Command\SetMembers',
6621
- 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
6622
-
6623
- /* commands operating on sorted sets */
6624
- 'ZADD' => 'Predis\Command\ZSetAdd',
6625
- 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
6626
- 'ZREM' => 'Predis\Command\ZSetRemove',
6627
- 'ZRANGE' => 'Predis\Command\ZSetRange',
6628
- 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
6629
- 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
6630
- 'ZCARD' => 'Predis\Command\ZSetCardinality',
6631
- 'ZSCORE' => 'Predis\Command\ZSetScore',
6632
- 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
6633
-
6634
- /* connection related commands */
6635
- 'PING' => 'Predis\Command\ConnectionPing',
6636
- 'AUTH' => 'Predis\Command\ConnectionAuth',
6637
- 'SELECT' => 'Predis\Command\ConnectionSelect',
6638
- 'ECHO' => 'Predis\Command\ConnectionEcho',
6639
- 'QUIT' => 'Predis\Command\ConnectionQuit',
6640
-
6641
- /* remote server control commands */
6642
- 'INFO' => 'Predis\Command\ServerInfoV26x',
6643
- 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
6644
- 'MONITOR' => 'Predis\Command\ServerMonitor',
6645
- 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
6646
- 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
6647
- 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
6648
- 'SAVE' => 'Predis\Command\ServerSave',
6649
- 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
6650
- 'LASTSAVE' => 'Predis\Command\ServerLastSave',
6651
- 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
6652
- 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
6653
-
6654
- /* ---------------- Redis 2.0 ---------------- */
6655
-
6656
- /* commands operating on string values */
6657
- 'SETEX' => 'Predis\Command\StringSetExpire',
6658
- 'APPEND' => 'Predis\Command\StringAppend',
6659
- 'SUBSTR' => 'Predis\Command\StringSubstr',
6660
-
6661
- /* commands operating on lists */
6662
- 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
6663
- 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
6664
-
6665
- /* commands operating on sorted sets */
6666
- 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
6667
- 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
6668
- 'ZCOUNT' => 'Predis\Command\ZSetCount',
6669
- 'ZRANK' => 'Predis\Command\ZSetRank',
6670
- 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
6671
- 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
6672
-
6673
- /* commands operating on hashes */
6674
- 'HSET' => 'Predis\Command\HashSet',
6675
- 'HSETNX' => 'Predis\Command\HashSetPreserve',
6676
- 'HMSET' => 'Predis\Command\HashSetMultiple',
6677
- 'HINCRBY' => 'Predis\Command\HashIncrementBy',
6678
- 'HGET' => 'Predis\Command\HashGet',
6679
- 'HMGET' => 'Predis\Command\HashGetMultiple',
6680
- 'HDEL' => 'Predis\Command\HashDelete',
6681
- 'HEXISTS' => 'Predis\Command\HashExists',
6682
- 'HLEN' => 'Predis\Command\HashLength',
6683
- 'HKEYS' => 'Predis\Command\HashKeys',
6684
- 'HVALS' => 'Predis\Command\HashValues',
6685
- 'HGETALL' => 'Predis\Command\HashGetAll',
6686
-
6687
- /* transactions */
6688
- 'MULTI' => 'Predis\Command\TransactionMulti',
6689
- 'EXEC' => 'Predis\Command\TransactionExec',
6690
- 'DISCARD' => 'Predis\Command\TransactionDiscard',
6691
-
6692
- /* publish - subscribe */
6693
- 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
6694
- 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
6695
- 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
6696
- 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
6697
- 'PUBLISH' => 'Predis\Command\PubSubPublish',
6698
-
6699
- /* remote server control commands */
6700
- 'CONFIG' => 'Predis\Command\ServerConfig',
6701
-
6702
- /* ---------------- Redis 2.2 ---------------- */
6703
-
6704
- /* commands operating on the key space */
6705
- 'PERSIST' => 'Predis\Command\KeyPersist',
6706
-
6707
- /* commands operating on string values */
6708
- 'STRLEN' => 'Predis\Command\StringStrlen',
6709
- 'SETRANGE' => 'Predis\Command\StringSetRange',
6710
- 'GETRANGE' => 'Predis\Command\StringGetRange',
6711
- 'SETBIT' => 'Predis\Command\StringSetBit',
6712
- 'GETBIT' => 'Predis\Command\StringGetBit',
6713
-
6714
- /* commands operating on lists */
6715
- 'RPUSHX' => 'Predis\Command\ListPushTailX',
6716
- 'LPUSHX' => 'Predis\Command\ListPushHeadX',
6717
- 'LINSERT' => 'Predis\Command\ListInsert',
6718
- 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
6719
-
6720
- /* commands operating on sorted sets */
6721
- 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
6722
-
6723
- /* transactions */
6724
- 'WATCH' => 'Predis\Command\TransactionWatch',
6725
- 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
6726
-
6727
- /* remote server control commands */
6728
- 'OBJECT' => 'Predis\Command\ServerObject',
6729
- 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
6730
-
6731
- /* ---------------- Redis 2.4 ---------------- */
6732
-
6733
- /* remote server control commands */
6734
- 'CLIENT' => 'Predis\Command\ServerClient',
6735
-
6736
- /* ---------------- Redis 2.6 ---------------- */
6737
-
6738
- /* commands operating on the key space */
6739
- 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
6740
- 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
6741
- 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
6742
- 'MIGRATE' => 'Predis\Command\KeyMigrate',
6743
-
6744
- /* commands operating on string values */
6745
- 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
6746
- 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
6747
- 'BITOP' => 'Predis\Command\StringBitOp',
6748
- 'BITCOUNT' => 'Predis\Command\StringBitCount',
6749
-
6750
- /* commands operating on hashes */
6751
- 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
6752
-
6753
- /* scripting */
6754
- 'EVAL' => 'Predis\Command\ServerEval',
6755
- 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
6756
- 'SCRIPT' => 'Predis\Command\ServerScript',
6757
-
6758
- /* remote server control commands */
6759
- 'TIME' => 'Predis\Command\ServerTime',
6760
- 'SENTINEL' => 'Predis\Command\ServerSentinel',
6761
-
6762
- /* ---------------- Redis 2.8 ---------------- */
6763
-
6764
- /* commands operating on the key space */
6765
- 'SCAN' => 'Predis\Command\KeyScan',
6766
-
6767
- /* commands operating on string values */
6768
- 'BITPOS' => 'Predis\Command\StringBitPos',
6769
-
6770
- /* commands operating on sets */
6771
- 'SSCAN' => 'Predis\Command\SetScan',
6772
-
6773
- /* commands operating on sorted sets */
6774
- 'ZSCAN' => 'Predis\Command\ZSetScan',
6775
- 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
6776
- 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
6777
- 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
6778
- 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',
6779
-
6780
- /* commands operating on hashes */
6781
- 'HSCAN' => 'Predis\Command\HashScan',
6782
-
6783
- /* publish - subscribe */
6784
- 'PUBSUB' => 'Predis\Command\PubSubPubsub',
6785
-
6786
- /* commands operating on HyperLogLog */
6787
- 'PFADD' => 'Predis\Command\HyperLogLogAdd',
6788
- 'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
6789
- 'PFMERGE' => 'Predis\Command\HyperLogLogMerge',
6790
-
6791
- /* remote server control commands */
6792
- 'COMMAND' => 'Predis\Command\ServerCommand',
6793
-
6794
- /* ---------------- Redis 3.2 ---------------- */
6795
-
6796
- /* commands operating on hashes */
6797
- 'HSTRLEN' => 'Predis\Command\HashStringLength',
6798
- 'BITFIELD' => 'Predis\Command\StringBitField',
6799
-
6800
- /* commands performing geospatial operations */
6801
- 'GEOADD' => 'Predis\Command\GeospatialGeoAdd',
6802
- 'GEOHASH' => 'Predis\Command\GeospatialGeoHash',
6803
- 'GEOPOS' => 'Predis\Command\GeospatialGeoPos',
6804
- 'GEODIST' => 'Predis\Command\GeospatialGeoDist',
6805
- 'GEORADIUS' => 'Predis\Command\GeospatialGeoRadius',
6806
- 'GEORADIUSBYMEMBER' => 'Predis\Command\GeospatialGeoRadiusByMember',
6807
- );
6808
- }
6809
- }
6810
-
6811
- /**
6812
- * Factory class for creating profile instances from strings.
6813
- *
6814
- * @author Daniele Alessandri <suppakilla@gmail.com>
6815
- */
6816
- final class Factory
6817
- {
6818
- private static $profiles = array(
6819
- '2.0' => 'Predis\Profile\RedisVersion200',
6820
- '2.2' => 'Predis\Profile\RedisVersion220',
6821
- '2.4' => 'Predis\Profile\RedisVersion240',
6822
- '2.6' => 'Predis\Profile\RedisVersion260',
6823
- '2.8' => 'Predis\Profile\RedisVersion280',
6824
- '3.0' => 'Predis\Profile\RedisVersion300',
6825
- '3.2' => 'Predis\Profile\RedisVersion320',
6826
- 'dev' => 'Predis\Profile\RedisUnstable',
6827
- 'default' => 'Predis\Profile\RedisVersion320',
6828
- );
6829
-
6830
- /**
6831
- *
6832
- */
6833
- private function __construct()
6834
- {
6835
- // NOOP
6836
- }
6837
-
6838
- /**
6839
- * Returns the default server profile.
6840
- *
6841
- * @return ProfileInterface
6842
- */
6843
- public static function getDefault()
6844
- {
6845
- return self::get('default');
6846
- }
6847
-
6848
- /**
6849
- * Returns the development server profile.
6850
- *
6851
- * @return ProfileInterface
6852
- */
6853
- public static function getDevelopment()
6854
- {
6855
- return self::get('dev');
6856
- }
6857
-
6858
- /**
6859
- * Registers a new server profile.
6860
- *
6861
- * @param string $alias Profile version or alias.
6862
- * @param string $class FQN of a class implementing Predis\Profile\ProfileInterface.
6863
- *
6864
- * @throws \InvalidArgumentException
6865
- */
6866
- public static function define($alias, $class)
6867
- {
6868
- $reflection = new \ReflectionClass($class);
6869
-
6870
- if (!$reflection->isSubclassOf('Predis\Profile\ProfileInterface')) {
6871
- throw new \InvalidArgumentException("The class '$class' is not a valid profile class.");
6872
- }
6873
-
6874
- self::$profiles[$alias] = $class;
6875
- }
6876
-
6877
- /**
6878
- * Returns the specified server profile.
6879
- *
6880
- * @param string $version Profile version or alias.
6881
- *
6882
- * @throws ClientException
6883
- *
6884
- * @return ProfileInterface
6885
- */
6886
- public static function get($version)
6887
- {
6888
- if (!isset(self::$profiles[$version])) {
6889
- throw new ClientException("Unknown server profile: '$version'.");
6890
- }
6891
-
6892
- $profile = self::$profiles[$version];
6893
-
6894
- return new $profile();
6895
- }
6896
- }
6897
-
6898
- /**
6899
- * Server profile for the current unstable version of Redis.
6900
- *
6901
- * @author Daniele Alessandri <suppakilla@gmail.com>
6902
- */
6903
- class RedisUnstable extends RedisVersion320
6904
- {
6905
- /**
6906
- * {@inheritdoc}
6907
- */
6908
- public function getVersion()
6909
- {
6910
- return '3.2';
6911
- }
6912
-
6913
- /**
6914
- * {@inheritdoc}
6915
- */
6916
- public function getSupportedCommands()
6917
- {
6918
- return array_merge(parent::getSupportedCommands(), array(
6919
- // EMPTY
6920
- ));
6921
- }
6922
- }
6923
-
6924
- /**
6925
- * Server profile for Redis 2.0.
6926
- *
6927
- * @author Daniele Alessandri <suppakilla@gmail.com>
6928
- */
6929
- class RedisVersion200 extends RedisProfile
6930
- {
6931
- /**
6932
- * {@inheritdoc}
6933
- */
6934
- public function getVersion()
6935
- {
6936
- return '2.0';
6937
- }
6938
-
6939
- /**
6940
- * {@inheritdoc}
6941
- */
6942
- public function getSupportedCommands()
6943
- {
6944
- return array(
6945
- /* ---------------- Redis 1.2 ---------------- */
6946
-
6947
- /* commands operating on the key space */
6948
- 'EXISTS' => 'Predis\Command\KeyExists',
6949
- 'DEL' => 'Predis\Command\KeyDelete',
6950
- 'TYPE' => 'Predis\Command\KeyType',
6951
- 'KEYS' => 'Predis\Command\KeyKeys',
6952
- 'RANDOMKEY' => 'Predis\Command\KeyRandom',
6953
- 'RENAME' => 'Predis\Command\KeyRename',
6954
- 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
6955
- 'EXPIRE' => 'Predis\Command\KeyExpire',
6956
- 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
6957
- 'TTL' => 'Predis\Command\KeyTimeToLive',
6958
- 'MOVE' => 'Predis\Command\KeyMove',
6959
- 'SORT' => 'Predis\Command\KeySort',
6960
-
6961
- /* commands operating on string values */
6962
- 'SET' => 'Predis\Command\StringSet',
6963
- 'SETNX' => 'Predis\Command\StringSetPreserve',
6964
- 'MSET' => 'Predis\Command\StringSetMultiple',
6965
- 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
6966
- 'GET' => 'Predis\Command\StringGet',
6967
- 'MGET' => 'Predis\Command\StringGetMultiple',
6968
- 'GETSET' => 'Predis\Command\StringGetSet',
6969
- 'INCR' => 'Predis\Command\StringIncrement',
6970
- 'INCRBY' => 'Predis\Command\StringIncrementBy',
6971
- 'DECR' => 'Predis\Command\StringDecrement',
6972
- 'DECRBY' => 'Predis\Command\StringDecrementBy',
6973
-
6974
- /* commands operating on lists */
6975
- 'RPUSH' => 'Predis\Command\ListPushTail',
6976
- 'LPUSH' => 'Predis\Command\ListPushHead',
6977
- 'LLEN' => 'Predis\Command\ListLength',
6978
- 'LRANGE' => 'Predis\Command\ListRange',
6979
- 'LTRIM' => 'Predis\Command\ListTrim',
6980
- 'LINDEX' => 'Predis\Command\ListIndex',
6981
- 'LSET' => 'Predis\Command\ListSet',
6982
- 'LREM' => 'Predis\Command\ListRemove',
6983
- 'LPOP' => 'Predis\Command\ListPopFirst',
6984
- 'RPOP' => 'Predis\Command\ListPopLast',
6985
- 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
6986
-
6987
- /* commands operating on sets */
6988
- 'SADD' => 'Predis\Command\SetAdd',
6989
- 'SREM' => 'Predis\Command\SetRemove',
6990
- 'SPOP' => 'Predis\Command\SetPop',
6991
- 'SMOVE' => 'Predis\Command\SetMove',
6992
- 'SCARD' => 'Predis\Command\SetCardinality',
6993
- 'SISMEMBER' => 'Predis\Command\SetIsMember',
6994
- 'SINTER' => 'Predis\Command\SetIntersection',
6995
- 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
6996
- 'SUNION' => 'Predis\Command\SetUnion',
6997
- 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
6998
- 'SDIFF' => 'Predis\Command\SetDifference',
6999
- 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
7000
- 'SMEMBERS' => 'Predis\Command\SetMembers',
7001
- 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
7002
-
7003
- /* commands operating on sorted sets */
7004
- 'ZADD' => 'Predis\Command\ZSetAdd',
7005
- 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
7006
- 'ZREM' => 'Predis\Command\ZSetRemove',
7007
- 'ZRANGE' => 'Predis\Command\ZSetRange',
7008
- 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
7009
- 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
7010
- 'ZCARD' => 'Predis\Command\ZSetCardinality',
7011
- 'ZSCORE' => 'Predis\Command\ZSetScore',
7012
- 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
7013
-
7014
- /* connection related commands */
7015
- 'PING' => 'Predis\Command\ConnectionPing',
7016
- 'AUTH' => 'Predis\Command\ConnectionAuth',
7017
- 'SELECT' => 'Predis\Command\ConnectionSelect',
7018
- 'ECHO' => 'Predis\Command\ConnectionEcho',
7019
- 'QUIT' => 'Predis\Command\ConnectionQuit',
7020
-
7021
- /* remote server control commands */
7022
- 'INFO' => 'Predis\Command\ServerInfo',
7023
- 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
7024
- 'MONITOR' => 'Predis\Command\ServerMonitor',
7025
- 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
7026
- 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
7027
- 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
7028
- 'SAVE' => 'Predis\Command\ServerSave',
7029
- 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
7030
- 'LASTSAVE' => 'Predis\Command\ServerLastSave',
7031
- 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
7032
- 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
7033
-
7034
- /* ---------------- Redis 2.0 ---------------- */
7035
-
7036
- /* commands operating on string values */
7037
- 'SETEX' => 'Predis\Command\StringSetExpire',
7038
- 'APPEND' => 'Predis\Command\StringAppend',
7039
- 'SUBSTR' => 'Predis\Command\StringSubstr',
7040
-
7041
- /* commands operating on lists */
7042
- 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
7043
- 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
7044
-
7045
- /* commands operating on sorted sets */
7046
- 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
7047
- 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
7048
- 'ZCOUNT' => 'Predis\Command\ZSetCount',
7049
- 'ZRANK' => 'Predis\Command\ZSetRank',
7050
- 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
7051
- 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
7052
-
7053
- /* commands operating on hashes */
7054
- 'HSET' => 'Predis\Command\HashSet',
7055
- 'HSETNX' => 'Predis\Command\HashSetPreserve',
7056
- 'HMSET' => 'Predis\Command\HashSetMultiple',
7057
- 'HINCRBY' => 'Predis\Command\HashIncrementBy',
7058
- 'HGET' => 'Predis\Command\HashGet',
7059
- 'HMGET' => 'Predis\Command\HashGetMultiple',
7060
- 'HDEL' => 'Predis\Command\HashDelete',
7061
- 'HEXISTS' => 'Predis\Command\HashExists',
7062
- 'HLEN' => 'Predis\Command\HashLength',
7063
- 'HKEYS' => 'Predis\Command\HashKeys',
7064
- 'HVALS' => 'Predis\Command\HashValues',
7065
- 'HGETALL' => 'Predis\Command\HashGetAll',
7066
-
7067
- /* transactions */
7068
- 'MULTI' => 'Predis\Command\TransactionMulti',
7069
- 'EXEC' => 'Predis\Command\TransactionExec',
7070
- 'DISCARD' => 'Predis\Command\TransactionDiscard',
7071
-
7072
- /* publish - subscribe */
7073
- 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
7074
- 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
7075
- 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
7076
- 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
7077
- 'PUBLISH' => 'Predis\Command\PubSubPublish',
7078
-
7079
- /* remote server control commands */
7080
- 'CONFIG' => 'Predis\Command\ServerConfig',
7081
- );
7082
- }
7083
- }
7084
-
7085
- /**
7086
- * Server profile for Redis 3.0.
7087
- *
7088
- * @author Daniele Alessandri <suppakilla@gmail.com>
7089
- */
7090
- class RedisVersion300 extends RedisProfile
7091
- {
7092
- /**
7093
- * {@inheritdoc}
7094
- */
7095
- public function getVersion()
7096
- {
7097
- return '3.0';
7098
- }
7099
-
7100
- /**
7101
- * {@inheritdoc}
7102
- */
7103
- public function getSupportedCommands()
7104
- {
7105
- return array(
7106
- /* ---------------- Redis 1.2 ---------------- */
7107
-
7108
- /* commands operating on the key space */
7109
- 'EXISTS' => 'Predis\Command\KeyExists',
7110
- 'DEL' => 'Predis\Command\KeyDelete',
7111
- 'TYPE' => 'Predis\Command\KeyType',
7112
- 'KEYS' => 'Predis\Command\KeyKeys',
7113
- 'RANDOMKEY' => 'Predis\Command\KeyRandom',
7114
- 'RENAME' => 'Predis\Command\KeyRename',
7115
- 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
7116
- 'EXPIRE' => 'Predis\Command\KeyExpire',
7117
- 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
7118
- 'TTL' => 'Predis\Command\KeyTimeToLive',
7119
- 'MOVE' => 'Predis\Command\KeyMove',
7120
- 'SORT' => 'Predis\Command\KeySort',
7121
- 'DUMP' => 'Predis\Command\KeyDump',
7122
- 'RESTORE' => 'Predis\Command\KeyRestore',
7123
-
7124
- /* commands operating on string values */
7125
- 'SET' => 'Predis\Command\StringSet',
7126
- 'SETNX' => 'Predis\Command\StringSetPreserve',
7127
- 'MSET' => 'Predis\Command\StringSetMultiple',
7128
- 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
7129
- 'GET' => 'Predis\Command\StringGet',
7130
- 'MGET' => 'Predis\Command\StringGetMultiple',
7131
- 'GETSET' => 'Predis\Command\StringGetSet',
7132
- 'INCR' => 'Predis\Command\StringIncrement',
7133
- 'INCRBY' => 'Predis\Command\StringIncrementBy',
7134
- 'DECR' => 'Predis\Command\StringDecrement',
7135
- 'DECRBY' => 'Predis\Command\StringDecrementBy',
7136
-
7137
- /* commands operating on lists */
7138
- 'RPUSH' => 'Predis\Command\ListPushTail',
7139
- 'LPUSH' => 'Predis\Command\ListPushHead',
7140
- 'LLEN' => 'Predis\Command\ListLength',
7141
- 'LRANGE' => 'Predis\Command\ListRange',
7142
- 'LTRIM' => 'Predis\Command\ListTrim',
7143
- 'LINDEX' => 'Predis\Command\ListIndex',
7144
- 'LSET' => 'Predis\Command\ListSet',
7145
- 'LREM' => 'Predis\Command\ListRemove',
7146
- 'LPOP' => 'Predis\Command\ListPopFirst',
7147
- 'RPOP' => 'Predis\Command\ListPopLast',
7148
- 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
7149
-
7150
- /* commands operating on sets */
7151
- 'SADD' => 'Predis\Command\SetAdd',
7152
- 'SREM' => 'Predis\Command\SetRemove',
7153
- 'SPOP' => 'Predis\Command\SetPop',
7154
- 'SMOVE' => 'Predis\Command\SetMove',
7155
- 'SCARD' => 'Predis\Command\SetCardinality',
7156
- 'SISMEMBER' => 'Predis\Command\SetIsMember',
7157
- 'SINTER' => 'Predis\Command\SetIntersection',
7158
- 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
7159
- 'SUNION' => 'Predis\Command\SetUnion',
7160
- 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
7161
- 'SDIFF' => 'Predis\Command\SetDifference',
7162
- 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
7163
- 'SMEMBERS' => 'Predis\Command\SetMembers',
7164
- 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
7165
-
7166
- /* commands operating on sorted sets */
7167
- 'ZADD' => 'Predis\Command\ZSetAdd',
7168
- 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
7169
- 'ZREM' => 'Predis\Command\ZSetRemove',
7170
- 'ZRANGE' => 'Predis\Command\ZSetRange',
7171
- 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
7172
- 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
7173
- 'ZCARD' => 'Predis\Command\ZSetCardinality',
7174
- 'ZSCORE' => 'Predis\Command\ZSetScore',
7175
- 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
7176
-
7177
- /* connection related commands */
7178
- 'PING' => 'Predis\Command\ConnectionPing',
7179
- 'AUTH' => 'Predis\Command\ConnectionAuth',
7180
- 'SELECT' => 'Predis\Command\ConnectionSelect',
7181
- 'ECHO' => 'Predis\Command\ConnectionEcho',
7182
- 'QUIT' => 'Predis\Command\ConnectionQuit',
7183
-
7184
- /* remote server control commands */
7185
- 'INFO' => 'Predis\Command\ServerInfoV26x',
7186
- 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
7187
- 'MONITOR' => 'Predis\Command\ServerMonitor',
7188
- 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
7189
- 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
7190
- 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
7191
- 'SAVE' => 'Predis\Command\ServerSave',
7192
- 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
7193
- 'LASTSAVE' => 'Predis\Command\ServerLastSave',
7194
- 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
7195
- 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
7196
-
7197
- /* ---------------- Redis 2.0 ---------------- */
7198
-
7199
- /* commands operating on string values */
7200
- 'SETEX' => 'Predis\Command\StringSetExpire',
7201
- 'APPEND' => 'Predis\Command\StringAppend',
7202
- 'SUBSTR' => 'Predis\Command\StringSubstr',
7203
-
7204
- /* commands operating on lists */
7205
- 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
7206
- 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
7207
-
7208
- /* commands operating on sorted sets */
7209
- 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
7210
- 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
7211
- 'ZCOUNT' => 'Predis\Command\ZSetCount',
7212
- 'ZRANK' => 'Predis\Command\ZSetRank',
7213
- 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
7214
- 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
7215
-
7216
- /* commands operating on hashes */
7217
- 'HSET' => 'Predis\Command\HashSet',
7218
- 'HSETNX' => 'Predis\Command\HashSetPreserve',
7219
- 'HMSET' => 'Predis\Command\HashSetMultiple',
7220
- 'HINCRBY' => 'Predis\Command\HashIncrementBy',
7221
- 'HGET' => 'Predis\Command\HashGet',
7222
- 'HMGET' => 'Predis\Command\HashGetMultiple',
7223
- 'HDEL' => 'Predis\Command\HashDelete',
7224
- 'HEXISTS' => 'Predis\Command\HashExists',
7225
- 'HLEN' => 'Predis\Command\HashLength',
7226
- 'HKEYS' => 'Predis\Command\HashKeys',
7227
- 'HVALS' => 'Predis\Command\HashValues',
7228
- 'HGETALL' => 'Predis\Command\HashGetAll',
7229
-
7230
- /* transactions */
7231
- 'MULTI' => 'Predis\Command\TransactionMulti',
7232
- 'EXEC' => 'Predis\Command\TransactionExec',
7233
- 'DISCARD' => 'Predis\Command\TransactionDiscard',
7234
-
7235
- /* publish - subscribe */
7236
- 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
7237
- 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
7238
- 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
7239
- 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
7240
- 'PUBLISH' => 'Predis\Command\PubSubPublish',
7241
-
7242
- /* remote server control commands */
7243
- 'CONFIG' => 'Predis\Command\ServerConfig',
7244
-
7245
- /* ---------------- Redis 2.2 ---------------- */
7246
-
7247
- /* commands operating on the key space */
7248
- 'PERSIST' => 'Predis\Command\KeyPersist',
7249
-
7250
- /* commands operating on string values */
7251
- 'STRLEN' => 'Predis\Command\StringStrlen',
7252
- 'SETRANGE' => 'Predis\Command\StringSetRange',
7253
- 'GETRANGE' => 'Predis\Command\StringGetRange',
7254
- 'SETBIT' => 'Predis\Command\StringSetBit',
7255
- 'GETBIT' => 'Predis\Command\StringGetBit',
7256
-
7257
- /* commands operating on lists */
7258
- 'RPUSHX' => 'Predis\Command\ListPushTailX',
7259
- 'LPUSHX' => 'Predis\Command\ListPushHeadX',
7260
- 'LINSERT' => 'Predis\Command\ListInsert',
7261
- 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
7262
-
7263
- /* commands operating on sorted sets */
7264
- 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
7265
-
7266
- /* transactions */
7267
- 'WATCH' => 'Predis\Command\TransactionWatch',
7268
- 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
7269
-
7270
- /* remote server control commands */
7271
- 'OBJECT' => 'Predis\Command\ServerObject',
7272
- 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
7273
-
7274
- /* ---------------- Redis 2.4 ---------------- */
7275
-
7276
- /* remote server control commands */
7277
- 'CLIENT' => 'Predis\Command\ServerClient',
7278
-
7279
- /* ---------------- Redis 2.6 ---------------- */
7280
-
7281
- /* commands operating on the key space */
7282
- 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
7283
- 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
7284
- 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
7285
- 'MIGRATE' => 'Predis\Command\KeyMigrate',
7286
-
7287
- /* commands operating on string values */
7288
- 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
7289
- 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
7290
- 'BITOP' => 'Predis\Command\StringBitOp',
7291
- 'BITCOUNT' => 'Predis\Command\StringBitCount',
7292
-
7293
- /* commands operating on hashes */
7294
- 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
7295
-
7296
- /* scripting */
7297
- 'EVAL' => 'Predis\Command\ServerEval',
7298
- 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
7299
- 'SCRIPT' => 'Predis\Command\ServerScript',
7300
-
7301
- /* remote server control commands */
7302
- 'TIME' => 'Predis\Command\ServerTime',
7303
- 'SENTINEL' => 'Predis\Command\ServerSentinel',
7304
-
7305
- /* ---------------- Redis 2.8 ---------------- */
7306
-
7307
- /* commands operating on the key space */
7308
- 'SCAN' => 'Predis\Command\KeyScan',
7309
-
7310
- /* commands operating on string values */
7311
- 'BITPOS' => 'Predis\Command\StringBitPos',
7312
-
7313
- /* commands operating on sets */
7314
- 'SSCAN' => 'Predis\Command\SetScan',
7315
-
7316
- /* commands operating on sorted sets */
7317
- 'ZSCAN' => 'Predis\Command\ZSetScan',
7318
- 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
7319
- 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
7320
- 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
7321
- 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',
7322
-
7323
- /* commands operating on hashes */
7324
- 'HSCAN' => 'Predis\Command\HashScan',
7325
-
7326
- /* publish - subscribe */
7327
- 'PUBSUB' => 'Predis\Command\PubSubPubsub',
7328
-
7329
- /* commands operating on HyperLogLog */
7330
- 'PFADD' => 'Predis\Command\HyperLogLogAdd',
7331
- 'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
7332
- 'PFMERGE' => 'Predis\Command\HyperLogLogMerge',
7333
-
7334
- /* remote server control commands */
7335
- 'COMMAND' => 'Predis\Command\ServerCommand',
7336
-
7337
- /* ---------------- Redis 3.0 ---------------- */
7338
-
7339
- );
7340
- }
7341
- }
7342
-
7343
- /**
7344
- * Server profile for Redis 2.6.
7345
- *
7346
- * @author Daniele Alessandri <suppakilla@gmail.com>
7347
- */
7348
- class RedisVersion260 extends RedisProfile
7349
- {
7350
- /**
7351
- * {@inheritdoc}
7352
- */
7353
- public function getVersion()
7354
- {
7355
- return '2.6';
7356
- }
7357
-
7358
- /**
7359
- * {@inheritdoc}
7360
- */
7361
- public function getSupportedCommands()
7362
- {
7363
- return array(
7364
- /* ---------------- Redis 1.2 ---------------- */
7365
-
7366
- /* commands operating on the key space */
7367
- 'EXISTS' => 'Predis\Command\KeyExists',
7368
- 'DEL' => 'Predis\Command\KeyDelete',
7369
- 'TYPE' => 'Predis\Command\KeyType',
7370
- 'KEYS' => 'Predis\Command\KeyKeys',
7371
- 'RANDOMKEY' => 'Predis\Command\KeyRandom',
7372
- 'RENAME' => 'Predis\Command\KeyRename',
7373
- 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
7374
- 'EXPIRE' => 'Predis\Command\KeyExpire',
7375
- 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
7376
- 'TTL' => 'Predis\Command\KeyTimeToLive',
7377
- 'MOVE' => 'Predis\Command\KeyMove',
7378
- 'SORT' => 'Predis\Command\KeySort',
7379
- 'DUMP' => 'Predis\Command\KeyDump',
7380
- 'RESTORE' => 'Predis\Command\KeyRestore',
7381
-
7382
- /* commands operating on string values */
7383
- 'SET' => 'Predis\Command\StringSet',
7384
- 'SETNX' => 'Predis\Command\StringSetPreserve',
7385
- 'MSET' => 'Predis\Command\StringSetMultiple',
7386
- 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
7387
- 'GET' => 'Predis\Command\StringGet',
7388
- 'MGET' => 'Predis\Command\StringGetMultiple',
7389
- 'GETSET' => 'Predis\Command\StringGetSet',
7390
- 'INCR' => 'Predis\Command\StringIncrement',
7391
- 'INCRBY' => 'Predis\Command\StringIncrementBy',
7392
- 'DECR' => 'Predis\Command\StringDecrement',
7393
- 'DECRBY' => 'Predis\Command\StringDecrementBy',
7394
-
7395
- /* commands operating on lists */
7396
- 'RPUSH' => 'Predis\Command\ListPushTail',
7397
- 'LPUSH' => 'Predis\Command\ListPushHead',
7398
- 'LLEN' => 'Predis\Command\ListLength',
7399
- 'LRANGE' => 'Predis\Command\ListRange',
7400
- 'LTRIM' => 'Predis\Command\ListTrim',
7401
- 'LINDEX' => 'Predis\Command\ListIndex',
7402
- 'LSET' => 'Predis\Command\ListSet',
7403
- 'LREM' => 'Predis\Command\ListRemove',
7404
- 'LPOP' => 'Predis\Command\ListPopFirst',
7405
- 'RPOP' => 'Predis\Command\ListPopLast',
7406
- 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
7407
-
7408
- /* commands operating on sets */
7409
- 'SADD' => 'Predis\Command\SetAdd',
7410
- 'SREM' => 'Predis\Command\SetRemove',
7411
- 'SPOP' => 'Predis\Command\SetPop',
7412
- 'SMOVE' => 'Predis\Command\SetMove',
7413
- 'SCARD' => 'Predis\Command\SetCardinality',
7414
- 'SISMEMBER' => 'Predis\Command\SetIsMember',
7415
- 'SINTER' => 'Predis\Command\SetIntersection',
7416
- 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
7417
- 'SUNION' => 'Predis\Command\SetUnion',
7418
- 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
7419
- 'SDIFF' => 'Predis\Command\SetDifference',
7420
- 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
7421
- 'SMEMBERS' => 'Predis\Command\SetMembers',
7422
- 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
7423
-
7424
- /* commands operating on sorted sets */
7425
- 'ZADD' => 'Predis\Command\ZSetAdd',
7426
- 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
7427
- 'ZREM' => 'Predis\Command\ZSetRemove',
7428
- 'ZRANGE' => 'Predis\Command\ZSetRange',
7429
- 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
7430
- 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
7431
- 'ZCARD' => 'Predis\Command\ZSetCardinality',
7432
- 'ZSCORE' => 'Predis\Command\ZSetScore',
7433
- 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
7434
-
7435
- /* connection related commands */
7436
- 'PING' => 'Predis\Command\ConnectionPing',
7437
- 'AUTH' => 'Predis\Command\ConnectionAuth',
7438
- 'SELECT' => 'Predis\Command\ConnectionSelect',
7439
- 'ECHO' => 'Predis\Command\ConnectionEcho',
7440
- 'QUIT' => 'Predis\Command\ConnectionQuit',
7441
-
7442
- /* remote server control commands */
7443
- 'INFO' => 'Predis\Command\ServerInfoV26x',
7444
- 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
7445
- 'MONITOR' => 'Predis\Command\ServerMonitor',
7446
- 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
7447
- 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
7448
- 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
7449
- 'SAVE' => 'Predis\Command\ServerSave',
7450
- 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
7451
- 'LASTSAVE' => 'Predis\Command\ServerLastSave',
7452
- 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
7453
- 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
7454
-
7455
- /* ---------------- Redis 2.0 ---------------- */
7456
-
7457
- /* commands operating on string values */
7458
- 'SETEX' => 'Predis\Command\StringSetExpire',
7459
- 'APPEND' => 'Predis\Command\StringAppend',
7460
- 'SUBSTR' => 'Predis\Command\StringSubstr',
7461
-
7462
- /* commands operating on lists */
7463
- 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
7464
- 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
7465
-
7466
- /* commands operating on sorted sets */
7467
- 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
7468
- 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
7469
- 'ZCOUNT' => 'Predis\Command\ZSetCount',
7470
- 'ZRANK' => 'Predis\Command\ZSetRank',
7471
- 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
7472
- 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
7473
-
7474
- /* commands operating on hashes */
7475
- 'HSET' => 'Predis\Command\HashSet',
7476
- 'HSETNX' => 'Predis\Command\HashSetPreserve',
7477
- 'HMSET' => 'Predis\Command\HashSetMultiple',
7478
- 'HINCRBY' => 'Predis\Command\HashIncrementBy',
7479
- 'HGET' => 'Predis\Command\HashGet',
7480
- 'HMGET' => 'Predis\Command\HashGetMultiple',
7481
- 'HDEL' => 'Predis\Command\HashDelete',
7482
- 'HEXISTS' => 'Predis\Command\HashExists',
7483
- 'HLEN' => 'Predis\Command\HashLength',
7484
- 'HKEYS' => 'Predis\Command\HashKeys',
7485
- 'HVALS' => 'Predis\Command\HashValues',
7486
- 'HGETALL' => 'Predis\Command\HashGetAll',
7487
-
7488
- /* transactions */
7489
- 'MULTI' => 'Predis\Command\TransactionMulti',
7490
- 'EXEC' => 'Predis\Command\TransactionExec',
7491
- 'DISCARD' => 'Predis\Command\TransactionDiscard',
7492
-
7493
- /* publish - subscribe */
7494
- 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
7495
- 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
7496
- 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
7497
- 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
7498
- 'PUBLISH' => 'Predis\Command\PubSubPublish',
7499
-
7500
- /* remote server control commands */
7501
- 'CONFIG' => 'Predis\Command\ServerConfig',
7502
-
7503
- /* ---------------- Redis 2.2 ---------------- */
7504
-
7505
- /* commands operating on the key space */
7506
- 'PERSIST' => 'Predis\Command\KeyPersist',
7507
-
7508
- /* commands operating on string values */
7509
- 'STRLEN' => 'Predis\Command\StringStrlen',
7510
- 'SETRANGE' => 'Predis\Command\StringSetRange',
7511
- 'GETRANGE' => 'Predis\Command\StringGetRange',
7512
- 'SETBIT' => 'Predis\Command\StringSetBit',
7513
- 'GETBIT' => 'Predis\Command\StringGetBit',
7514
-
7515
- /* commands operating on lists */
7516
- 'RPUSHX' => 'Predis\Command\ListPushTailX',
7517
- 'LPUSHX' => 'Predis\Command\ListPushHeadX',
7518
- 'LINSERT' => 'Predis\Command\ListInsert',
7519
- 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
7520
-
7521
- /* commands operating on sorted sets */
7522
- 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
7523
-
7524
- /* transactions */
7525
- 'WATCH' => 'Predis\Command\TransactionWatch',
7526
- 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
7527
-
7528
- /* remote server control commands */
7529
- 'OBJECT' => 'Predis\Command\ServerObject',
7530
- 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
7531
-
7532
- /* ---------------- Redis 2.4 ---------------- */
7533
-
7534
- /* remote server control commands */
7535
- 'CLIENT' => 'Predis\Command\ServerClient',
7536
-
7537
- /* ---------------- Redis 2.6 ---------------- */
7538
-
7539
- /* commands operating on the key space */
7540
- 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
7541
- 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
7542
- 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
7543
- 'MIGRATE' => 'Predis\Command\KeyMigrate',
7544
-
7545
- /* commands operating on string values */
7546
- 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
7547
- 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
7548
- 'BITOP' => 'Predis\Command\StringBitOp',
7549
- 'BITCOUNT' => 'Predis\Command\StringBitCount',
7550
-
7551
- /* commands operating on hashes */
7552
- 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
7553
-
7554
- /* scripting */
7555
- 'EVAL' => 'Predis\Command\ServerEval',
7556
- 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
7557
- 'SCRIPT' => 'Predis\Command\ServerScript',
7558
-
7559
- /* remote server control commands */
7560
- 'TIME' => 'Predis\Command\ServerTime',
7561
- 'SENTINEL' => 'Predis\Command\ServerSentinel',
7562
- );
7563
- }
7564
- }
7565
-
7566
- /**
7567
- * Server profile for Redis 2.4.
7568
- *
7569
- * @author Daniele Alessandri <suppakilla@gmail.com>
7570
- */
7571
- class RedisVersion240 extends RedisProfile
7572
- {
7573
- /**
7574
- * {@inheritdoc}
7575
- */
7576
- public function getVersion()
7577
- {
7578
- return '2.4';
7579
- }
7580
-
7581
- /**
7582
- * {@inheritdoc}
7583
- */
7584
- public function getSupportedCommands()
7585
- {
7586
- return array(
7587
- /* ---------------- Redis 1.2 ---------------- */
7588
-
7589
- /* commands operating on the key space */
7590
- 'EXISTS' => 'Predis\Command\KeyExists',
7591
- 'DEL' => 'Predis\Command\KeyDelete',
7592
- 'TYPE' => 'Predis\Command\KeyType',
7593
- 'KEYS' => 'Predis\Command\KeyKeys',
7594
- 'RANDOMKEY' => 'Predis\Command\KeyRandom',
7595
- 'RENAME' => 'Predis\Command\KeyRename',
7596
- 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
7597
- 'EXPIRE' => 'Predis\Command\KeyExpire',
7598
- 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
7599
- 'TTL' => 'Predis\Command\KeyTimeToLive',
7600
- 'MOVE' => 'Predis\Command\KeyMove',
7601
- 'SORT' => 'Predis\Command\KeySort',
7602
-
7603
- /* commands operating on string values */
7604
- 'SET' => 'Predis\Command\StringSet',
7605
- 'SETNX' => 'Predis\Command\StringSetPreserve',
7606
- 'MSET' => 'Predis\Command\StringSetMultiple',
7607
- 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
7608
- 'GET' => 'Predis\Command\StringGet',
7609
- 'MGET' => 'Predis\Command\StringGetMultiple',
7610
- 'GETSET' => 'Predis\Command\StringGetSet',
7611
- 'INCR' => 'Predis\Command\StringIncrement',
7612
- 'INCRBY' => 'Predis\Command\StringIncrementBy',
7613
- 'DECR' => 'Predis\Command\StringDecrement',
7614
- 'DECRBY' => 'Predis\Command\StringDecrementBy',
7615
-
7616
- /* commands operating on lists */
7617
- 'RPUSH' => 'Predis\Command\ListPushTail',
7618
- 'LPUSH' => 'Predis\Command\ListPushHead',
7619
- 'LLEN' => 'Predis\Command\ListLength',
7620
- 'LRANGE' => 'Predis\Command\ListRange',
7621
- 'LTRIM' => 'Predis\Command\ListTrim',
7622
- 'LINDEX' => 'Predis\Command\ListIndex',
7623
- 'LSET' => 'Predis\Command\ListSet',
7624
- 'LREM' => 'Predis\Command\ListRemove',
7625
- 'LPOP' => 'Predis\Command\ListPopFirst',
7626
- 'RPOP' => 'Predis\Command\ListPopLast',
7627
- 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
7628
-
7629
- /* commands operating on sets */
7630
- 'SADD' => 'Predis\Command\SetAdd',
7631
- 'SREM' => 'Predis\Command\SetRemove',
7632
- 'SPOP' => 'Predis\Command\SetPop',
7633
- 'SMOVE' => 'Predis\Command\SetMove',
7634
- 'SCARD' => 'Predis\Command\SetCardinality',
7635
- 'SISMEMBER' => 'Predis\Command\SetIsMember',
7636
- 'SINTER' => 'Predis\Command\SetIntersection',
7637
- 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
7638
- 'SUNION' => 'Predis\Command\SetUnion',
7639
- 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
7640
- 'SDIFF' => 'Predis\Command\SetDifference',
7641
- 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
7642
- 'SMEMBERS' => 'Predis\Command\SetMembers',
7643
- 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
7644
-
7645
- /* commands operating on sorted sets */
7646
- 'ZADD' => 'Predis\Command\ZSetAdd',
7647
- 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
7648
- 'ZREM' => 'Predis\Command\ZSetRemove',
7649
- 'ZRANGE' => 'Predis\Command\ZSetRange',
7650
- 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
7651
- 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
7652
- 'ZCARD' => 'Predis\Command\ZSetCardinality',
7653
- 'ZSCORE' => 'Predis\Command\ZSetScore',
7654
- 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
7655
-
7656
- /* connection related commands */
7657
- 'PING' => 'Predis\Command\ConnectionPing',
7658
- 'AUTH' => 'Predis\Command\ConnectionAuth',
7659
- 'SELECT' => 'Predis\Command\ConnectionSelect',
7660
- 'ECHO' => 'Predis\Command\ConnectionEcho',
7661
- 'QUIT' => 'Predis\Command\ConnectionQuit',
7662
-
7663
- /* remote server control commands */
7664
- 'INFO' => 'Predis\Command\ServerInfo',
7665
- 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
7666
- 'MONITOR' => 'Predis\Command\ServerMonitor',
7667
- 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
7668
- 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
7669
- 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
7670
- 'SAVE' => 'Predis\Command\ServerSave',
7671
- 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
7672
- 'LASTSAVE' => 'Predis\Command\ServerLastSave',
7673
- 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
7674
- 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
7675
-
7676
- /* ---------------- Redis 2.0 ---------------- */
7677
-
7678
- /* commands operating on string values */
7679
- 'SETEX' => 'Predis\Command\StringSetExpire',
7680
- 'APPEND' => 'Predis\Command\StringAppend',
7681
- 'SUBSTR' => 'Predis\Command\StringSubstr',
7682
-
7683
- /* commands operating on lists */
7684
- 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
7685
- 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
7686
-
7687
- /* commands operating on sorted sets */
7688
- 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
7689
- 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
7690
- 'ZCOUNT' => 'Predis\Command\ZSetCount',
7691
- 'ZRANK' => 'Predis\Command\ZSetRank',
7692
- 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
7693
- 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
7694
-
7695
- /* commands operating on hashes */
7696
- 'HSET' => 'Predis\Command\HashSet',
7697
- 'HSETNX' => 'Predis\Command\HashSetPreserve',
7698
- 'HMSET' => 'Predis\Command\HashSetMultiple',
7699
- 'HINCRBY' => 'Predis\Command\HashIncrementBy',
7700
- 'HGET' => 'Predis\Command\HashGet',
7701
- 'HMGET' => 'Predis\Command\HashGetMultiple',
7702
- 'HDEL' => 'Predis\Command\HashDelete',
7703
- 'HEXISTS' => 'Predis\Command\HashExists',
7704
- 'HLEN' => 'Predis\Command\HashLength',
7705
- 'HKEYS' => 'Predis\Command\HashKeys',
7706
- 'HVALS' => 'Predis\Command\HashValues',
7707
- 'HGETALL' => 'Predis\Command\HashGetAll',
7708
-
7709
- /* transactions */
7710
- 'MULTI' => 'Predis\Command\TransactionMulti',
7711
- 'EXEC' => 'Predis\Command\TransactionExec',
7712
- 'DISCARD' => 'Predis\Command\TransactionDiscard',
7713
-
7714
- /* publish - subscribe */
7715
- 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
7716
- 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
7717
- 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
7718
- 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
7719
- 'PUBLISH' => 'Predis\Command\PubSubPublish',
7720
-
7721
- /* remote server control commands */
7722
- 'CONFIG' => 'Predis\Command\ServerConfig',
7723
-
7724
- /* ---------------- Redis 2.2 ---------------- */
7725
-
7726
- /* commands operating on the key space */
7727
- 'PERSIST' => 'Predis\Command\KeyPersist',
7728
-
7729
- /* commands operating on string values */
7730
- 'STRLEN' => 'Predis\Command\StringStrlen',
7731
- 'SETRANGE' => 'Predis\Command\StringSetRange',
7732
- 'GETRANGE' => 'Predis\Command\StringGetRange',
7733
- 'SETBIT' => 'Predis\Command\StringSetBit',
7734
- 'GETBIT' => 'Predis\Command\StringGetBit',
7735
-
7736
- /* commands operating on lists */
7737
- 'RPUSHX' => 'Predis\Command\ListPushTailX',
7738
- 'LPUSHX' => 'Predis\Command\ListPushHeadX',
7739
- 'LINSERT' => 'Predis\Command\ListInsert',
7740
- 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
7741
-
7742
- /* commands operating on sorted sets */
7743
- 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
7744
-
7745
- /* transactions */
7746
- 'WATCH' => 'Predis\Command\TransactionWatch',
7747
- 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
7748
-
7749
- /* remote server control commands */
7750
- 'OBJECT' => 'Predis\Command\ServerObject',
7751
- 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
7752
-
7753
- /* ---------------- Redis 2.4 ---------------- */
7754
-
7755
- /* remote server control commands */
7756
- 'CLIENT' => 'Predis\Command\ServerClient',
7757
- );
7758
- }
7759
- }
7760
-
7761
- /**
7762
- * Server profile for Redis 2.8.
7763
- *
7764
- * @author Daniele Alessandri <suppakilla@gmail.com>
7765
- */
7766
- class RedisVersion280 extends RedisProfile
7767
- {
7768
- /**
7769
- * {@inheritdoc}
7770
- */
7771
- public function getVersion()
7772
- {
7773
- return '2.8';
7774
- }
7775
-
7776
- /**
7777
- * {@inheritdoc}
7778
- */
7779
- public function getSupportedCommands()
7780
- {
7781
- return array(
7782
- /* ---------------- Redis 1.2 ---------------- */
7783
-
7784
- /* commands operating on the key space */
7785
- 'EXISTS' => 'Predis\Command\KeyExists',
7786
- 'DEL' => 'Predis\Command\KeyDelete',
7787
- 'TYPE' => 'Predis\Command\KeyType',
7788
- 'KEYS' => 'Predis\Command\KeyKeys',
7789
- 'RANDOMKEY' => 'Predis\Command\KeyRandom',
7790
- 'RENAME' => 'Predis\Command\KeyRename',
7791
- 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
7792
- 'EXPIRE' => 'Predis\Command\KeyExpire',
7793
- 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
7794
- 'TTL' => 'Predis\Command\KeyTimeToLive',
7795
- 'MOVE' => 'Predis\Command\KeyMove',
7796
- 'SORT' => 'Predis\Command\KeySort',
7797
- 'DUMP' => 'Predis\Command\KeyDump',
7798
- 'RESTORE' => 'Predis\Command\KeyRestore',
7799
-
7800
- /* commands operating on string values */
7801
- 'SET' => 'Predis\Command\StringSet',
7802
- 'SETNX' => 'Predis\Command\StringSetPreserve',
7803
- 'MSET' => 'Predis\Command\StringSetMultiple',
7804
- 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
7805
- 'GET' => 'Predis\Command\StringGet',
7806
- 'MGET' => 'Predis\Command\StringGetMultiple',
7807
- 'GETSET' => 'Predis\Command\StringGetSet',
7808
- 'INCR' => 'Predis\Command\StringIncrement',
7809
- 'INCRBY' => 'Predis\Command\StringIncrementBy',
7810
- 'DECR' => 'Predis\Command\StringDecrement',
7811
- 'DECRBY' => 'Predis\Command\StringDecrementBy',
7812
-
7813
- /* commands operating on lists */
7814
- 'RPUSH' => 'Predis\Command\ListPushTail',
7815
- 'LPUSH' => 'Predis\Command\ListPushHead',
7816
- 'LLEN' => 'Predis\Command\ListLength',
7817
- 'LRANGE' => 'Predis\Command\ListRange',
7818
- 'LTRIM' => 'Predis\Command\ListTrim',
7819
- 'LINDEX' => 'Predis\Command\ListIndex',
7820
- 'LSET' => 'Predis\Command\ListSet',
7821
- 'LREM' => 'Predis\Command\ListRemove',
7822
- 'LPOP' => 'Predis\Command\ListPopFirst',
7823
- 'RPOP' => 'Predis\Command\ListPopLast',
7824
- 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
7825
-
7826
- /* commands operating on sets */
7827
- 'SADD' => 'Predis\Command\SetAdd',
7828
- 'SREM' => 'Predis\Command\SetRemove',
7829
- 'SPOP' => 'Predis\Command\SetPop',
7830
- 'SMOVE' => 'Predis\Command\SetMove',
7831
- 'SCARD' => 'Predis\Command\SetCardinality',
7832
- 'SISMEMBER' => 'Predis\Command\SetIsMember',
7833
- 'SINTER' => 'Predis\Command\SetIntersection',
7834
- 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
7835
- 'SUNION' => 'Predis\Command\SetUnion',
7836
- 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
7837
- 'SDIFF' => 'Predis\Command\SetDifference',
7838
- 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
7839
- 'SMEMBERS' => 'Predis\Command\SetMembers',
7840
- 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
7841
-
7842
- /* commands operating on sorted sets */
7843
- 'ZADD' => 'Predis\Command\ZSetAdd',
7844
- 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
7845
- 'ZREM' => 'Predis\Command\ZSetRemove',
7846
- 'ZRANGE' => 'Predis\Command\ZSetRange',
7847
- 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
7848
- 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
7849
- 'ZCARD' => 'Predis\Command\ZSetCardinality',
7850
- 'ZSCORE' => 'Predis\Command\ZSetScore',
7851
- 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
7852
-
7853
- /* connection related commands */
7854
- 'PING' => 'Predis\Command\ConnectionPing',
7855
- 'AUTH' => 'Predis\Command\ConnectionAuth',
7856
- 'SELECT' => 'Predis\Command\ConnectionSelect',
7857
- 'ECHO' => 'Predis\Command\ConnectionEcho',
7858
- 'QUIT' => 'Predis\Command\ConnectionQuit',
7859
-
7860
- /* remote server control commands */
7861
- 'INFO' => 'Predis\Command\ServerInfoV26x',
7862
- 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
7863
- 'MONITOR' => 'Predis\Command\ServerMonitor',
7864
- 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
7865
- 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
7866
- 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
7867
- 'SAVE' => 'Predis\Command\ServerSave',
7868
- 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
7869
- 'LASTSAVE' => 'Predis\Command\ServerLastSave',
7870
- 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
7871
- 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
7872
-
7873
- /* ---------------- Redis 2.0 ---------------- */
7874
-
7875
- /* commands operating on string values */
7876
- 'SETEX' => 'Predis\Command\StringSetExpire',
7877
- 'APPEND' => 'Predis\Command\StringAppend',
7878
- 'SUBSTR' => 'Predis\Command\StringSubstr',
7879
-
7880
- /* commands operating on lists */
7881
- 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
7882
- 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
7883
-
7884
- /* commands operating on sorted sets */
7885
- 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
7886
- 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
7887
- 'ZCOUNT' => 'Predis\Command\ZSetCount',
7888
- 'ZRANK' => 'Predis\Command\ZSetRank',
7889
- 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
7890
- 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
7891
-
7892
- /* commands operating on hashes */
7893
- 'HSET' => 'Predis\Command\HashSet',
7894
- 'HSETNX' => 'Predis\Command\HashSetPreserve',
7895
- 'HMSET' => 'Predis\Command\HashSetMultiple',
7896
- 'HINCRBY' => 'Predis\Command\HashIncrementBy',
7897
- 'HGET' => 'Predis\Command\HashGet',
7898
- 'HMGET' => 'Predis\Command\HashGetMultiple',
7899
- 'HDEL' => 'Predis\Command\HashDelete',
7900
- 'HEXISTS' => 'Predis\Command\HashExists',
7901
- 'HLEN' => 'Predis\Command\HashLength',
7902
- 'HKEYS' => 'Predis\Command\HashKeys',
7903
- 'HVALS' => 'Predis\Command\HashValues',
7904
- 'HGETALL' => 'Predis\Command\HashGetAll',
7905
-
7906
- /* transactions */
7907
- 'MULTI' => 'Predis\Command\TransactionMulti',
7908
- 'EXEC' => 'Predis\Command\TransactionExec',
7909
- 'DISCARD' => 'Predis\Command\TransactionDiscard',
7910
-
7911
- /* publish - subscribe */
7912
- 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
7913
- 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
7914
- 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
7915
- 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
7916
- 'PUBLISH' => 'Predis\Command\PubSubPublish',
7917
-
7918
- /* remote server control commands */
7919
- 'CONFIG' => 'Predis\Command\ServerConfig',
7920
-
7921
- /* ---------------- Redis 2.2 ---------------- */
7922
-
7923
- /* commands operating on the key space */
7924
- 'PERSIST' => 'Predis\Command\KeyPersist',
7925
-
7926
- /* commands operating on string values */
7927
- 'STRLEN' => 'Predis\Command\StringStrlen',
7928
- 'SETRANGE' => 'Predis\Command\StringSetRange',
7929
- 'GETRANGE' => 'Predis\Command\StringGetRange',
7930
- 'SETBIT' => 'Predis\Command\StringSetBit',
7931
- 'GETBIT' => 'Predis\Command\StringGetBit',
7932
-
7933
- /* commands operating on lists */
7934
- 'RPUSHX' => 'Predis\Command\ListPushTailX',
7935
- 'LPUSHX' => 'Predis\Command\ListPushHeadX',
7936
- 'LINSERT' => 'Predis\Command\ListInsert',
7937
- 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
7938
-
7939
- /* commands operating on sorted sets */
7940
- 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
7941
-
7942
- /* transactions */
7943
- 'WATCH' => 'Predis\Command\TransactionWatch',
7944
- 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
7945
-
7946
- /* remote server control commands */
7947
- 'OBJECT' => 'Predis\Command\ServerObject',
7948
- 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
7949
-
7950
- /* ---------------- Redis 2.4 ---------------- */
7951
-
7952
- /* remote server control commands */
7953
- 'CLIENT' => 'Predis\Command\ServerClient',
7954
-
7955
- /* ---------------- Redis 2.6 ---------------- */
7956
-
7957
- /* commands operating on the key space */
7958
- 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
7959
- 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
7960
- 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
7961
- 'MIGRATE' => 'Predis\Command\KeyMigrate',
7962
-
7963
- /* commands operating on string values */
7964
- 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
7965
- 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
7966
- 'BITOP' => 'Predis\Command\StringBitOp',
7967
- 'BITCOUNT' => 'Predis\Command\StringBitCount',
7968
-
7969
- /* commands operating on hashes */
7970
- 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
7971
-
7972
- /* scripting */
7973
- 'EVAL' => 'Predis\Command\ServerEval',
7974
- 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
7975
- 'SCRIPT' => 'Predis\Command\ServerScript',
7976
-
7977
- /* remote server control commands */
7978
- 'TIME' => 'Predis\Command\ServerTime',
7979
- 'SENTINEL' => 'Predis\Command\ServerSentinel',
7980
-
7981
- /* ---------------- Redis 2.8 ---------------- */
7982
-
7983
- /* commands operating on the key space */
7984
- 'SCAN' => 'Predis\Command\KeyScan',
7985
-
7986
- /* commands operating on string values */
7987
- 'BITPOS' => 'Predis\Command\StringBitPos',
7988
-
7989
- /* commands operating on sets */
7990
- 'SSCAN' => 'Predis\Command\SetScan',
7991
-
7992
- /* commands operating on sorted sets */
7993
- 'ZSCAN' => 'Predis\Command\ZSetScan',
7994
- 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
7995
- 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
7996
- 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
7997
- 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',
7998
-
7999
- /* commands operating on hashes */
8000
- 'HSCAN' => 'Predis\Command\HashScan',
8001
-
8002
- /* publish - subscribe */
8003
- 'PUBSUB' => 'Predis\Command\PubSubPubsub',
8004
-
8005
- /* commands operating on HyperLogLog */
8006
- 'PFADD' => 'Predis\Command\HyperLogLogAdd',
8007
- 'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
8008
- 'PFMERGE' => 'Predis\Command\HyperLogLogMerge',
8009
-
8010
- /* remote server control commands */
8011
- 'COMMAND' => 'Predis\Command\ServerCommand',
8012
- );
8013
- }
8014
- }
8015
-
8016
- /**
8017
- * Server profile for Redis 2.2.
8018
- *
8019
- * @author Daniele Alessandri <suppakilla@gmail.com>
8020
- */
8021
- class RedisVersion220 extends RedisProfile
8022
- {
8023
- /**
8024
- * {@inheritdoc}
8025
- */
8026
- public function getVersion()
8027
- {
8028
- return '2.2';
8029
- }
8030
-
8031
- /**
8032
- * {@inheritdoc}
8033
- */
8034
- public function getSupportedCommands()
8035
- {
8036
- return array(
8037
- /* ---------------- Redis 1.2 ---------------- */
8038
-
8039
- /* commands operating on the key space */
8040
- 'EXISTS' => 'Predis\Command\KeyExists',
8041
- 'DEL' => 'Predis\Command\KeyDelete',
8042
- 'TYPE' => 'Predis\Command\KeyType',
8043
- 'KEYS' => 'Predis\Command\KeyKeys',
8044
- 'RANDOMKEY' => 'Predis\Command\KeyRandom',
8045
- 'RENAME' => 'Predis\Command\KeyRename',
8046
- 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
8047
- 'EXPIRE' => 'Predis\Command\KeyExpire',
8048
- 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
8049
- 'TTL' => 'Predis\Command\KeyTimeToLive',
8050
- 'MOVE' => 'Predis\Command\KeyMove',
8051
- 'SORT' => 'Predis\Command\KeySort',
8052
-
8053
- /* commands operating on string values */
8054
- 'SET' => 'Predis\Command\StringSet',
8055
- 'SETNX' => 'Predis\Command\StringSetPreserve',
8056
- 'MSET' => 'Predis\Command\StringSetMultiple',
8057
- 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
8058
- 'GET' => 'Predis\Command\StringGet',
8059
- 'MGET' => 'Predis\Command\StringGetMultiple',
8060
- 'GETSET' => 'Predis\Command\StringGetSet',
8061
- 'INCR' => 'Predis\Command\StringIncrement',
8062
- 'INCRBY' => 'Predis\Command\StringIncrementBy',
8063
- 'DECR' => 'Predis\Command\StringDecrement',
8064
- 'DECRBY' => 'Predis\Command\StringDecrementBy',
8065
-
8066
- /* commands operating on lists */
8067
- 'RPUSH' => 'Predis\Command\ListPushTail',
8068
- 'LPUSH' => 'Predis\Command\ListPushHead',
8069
- 'LLEN' => 'Predis\Command\ListLength',
8070
- 'LRANGE' => 'Predis\Command\ListRange',
8071
- 'LTRIM' => 'Predis\Command\ListTrim',
8072
- 'LINDEX' => 'Predis\Command\ListIndex',
8073
- 'LSET' => 'Predis\Command\ListSet',
8074
- 'LREM' => 'Predis\Command\ListRemove',
8075
- 'LPOP' => 'Predis\Command\ListPopFirst',
8076
- 'RPOP' => 'Predis\Command\ListPopLast',
8077
- 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
8078
-
8079
- /* commands operating on sets */
8080
- 'SADD' => 'Predis\Command\SetAdd',
8081
- 'SREM' => 'Predis\Command\SetRemove',
8082
- 'SPOP' => 'Predis\Command\SetPop',
8083
- 'SMOVE' => 'Predis\Command\SetMove',
8084
- 'SCARD' => 'Predis\Command\SetCardinality',
8085
- 'SISMEMBER' => 'Predis\Command\SetIsMember',
8086
- 'SINTER' => 'Predis\Command\SetIntersection',
8087
- 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
8088
- 'SUNION' => 'Predis\Command\SetUnion',
8089
- 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
8090
- 'SDIFF' => 'Predis\Command\SetDifference',
8091
- 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
8092
- 'SMEMBERS' => 'Predis\Command\SetMembers',
8093
- 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
8094
-
8095
- /* commands operating on sorted sets */
8096
- 'ZADD' => 'Predis\Command\ZSetAdd',
8097
- 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
8098
- 'ZREM' => 'Predis\Command\ZSetRemove',
8099
- 'ZRANGE' => 'Predis\Command\ZSetRange',
8100
- 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
8101
- 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
8102
- 'ZCARD' => 'Predis\Command\ZSetCardinality',
8103
- 'ZSCORE' => 'Predis\Command\ZSetScore',
8104
- 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
8105
-
8106
- /* connection related commands */
8107
- 'PING' => 'Predis\Command\ConnectionPing',
8108
- 'AUTH' => 'Predis\Command\ConnectionAuth',
8109
- 'SELECT' => 'Predis\Command\ConnectionSelect',
8110
- 'ECHO' => 'Predis\Command\ConnectionEcho',
8111
- 'QUIT' => 'Predis\Command\ConnectionQuit',
8112
-
8113
- /* remote server control commands */
8114
- 'INFO' => 'Predis\Command\ServerInfo',
8115
- 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
8116
- 'MONITOR' => 'Predis\Command\ServerMonitor',
8117
- 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
8118
- 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
8119
- 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
8120
- 'SAVE' => 'Predis\Command\ServerSave',
8121
- 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
8122
- 'LASTSAVE' => 'Predis\Command\ServerLastSave',
8123
- 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
8124
- 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
8125
-
8126
- /* ---------------- Redis 2.0 ---------------- */
8127
-
8128
- /* commands operating on string values */
8129
- 'SETEX' => 'Predis\Command\StringSetExpire',
8130
- 'APPEND' => 'Predis\Command\StringAppend',
8131
- 'SUBSTR' => 'Predis\Command\StringSubstr',
8132
-
8133
- /* commands operating on lists */
8134
- 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
8135
- 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
8136
-
8137
- /* commands operating on sorted sets */
8138
- 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
8139
- 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
8140
- 'ZCOUNT' => 'Predis\Command\ZSetCount',
8141
- 'ZRANK' => 'Predis\Command\ZSetRank',
8142
- 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
8143
- 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
8144
-
8145
- /* commands operating on hashes */
8146
- 'HSET' => 'Predis\Command\HashSet',
8147
- 'HSETNX' => 'Predis\Command\HashSetPreserve',
8148
- 'HMSET' => 'Predis\Command\HashSetMultiple',
8149
- 'HINCRBY' => 'Predis\Command\HashIncrementBy',
8150
- 'HGET' => 'Predis\Command\HashGet',
8151
- 'HMGET' => 'Predis\Command\HashGetMultiple',
8152
- 'HDEL' => 'Predis\Command\HashDelete',
8153
- 'HEXISTS' => 'Predis\Command\HashExists',
8154
- 'HLEN' => 'Predis\Command\HashLength',
8155
- 'HKEYS' => 'Predis\Command\HashKeys',
8156
- 'HVALS' => 'Predis\Command\HashValues',
8157
- 'HGETALL' => 'Predis\Command\HashGetAll',
8158
-
8159
- /* transactions */
8160
- 'MULTI' => 'Predis\Command\TransactionMulti',
8161
- 'EXEC' => 'Predis\Command\TransactionExec',
8162
- 'DISCARD' => 'Predis\Command\TransactionDiscard',
8163
-
8164
- /* publish - subscribe */
8165
- 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
8166
- 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
8167
- 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
8168
- 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
8169
- 'PUBLISH' => 'Predis\Command\PubSubPublish',
8170
-
8171
- /* remote server control commands */
8172
- 'CONFIG' => 'Predis\Command\ServerConfig',
8173
-
8174
- /* ---------------- Redis 2.2 ---------------- */
8175
-
8176
- /* commands operating on the key space */
8177
- 'PERSIST' => 'Predis\Command\KeyPersist',
8178
-
8179
- /* commands operating on string values */
8180
- 'STRLEN' => 'Predis\Command\StringStrlen',
8181
- 'SETRANGE' => 'Predis\Command\StringSetRange',
8182
- 'GETRANGE' => 'Predis\Command\StringGetRange',
8183
- 'SETBIT' => 'Predis\Command\StringSetBit',
8184
- 'GETBIT' => 'Predis\Command\StringGetBit',
8185
-
8186
- /* commands operating on lists */
8187
- 'RPUSHX' => 'Predis\Command\ListPushTailX',
8188
- 'LPUSHX' => 'Predis\Command\ListPushHeadX',
8189
- 'LINSERT' => 'Predis\Command\ListInsert',
8190
- 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
8191
-
8192
- /* commands operating on sorted sets */
8193
- 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
8194
-
8195
- /* transactions */
8196
- 'WATCH' => 'Predis\Command\TransactionWatch',
8197
- 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
8198
-
8199
- /* remote server control commands */
8200
- 'OBJECT' => 'Predis\Command\ServerObject',
8201
- 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
8202
- );
8203
- }
8204
- }
8205
-
8206
- /* --------------------------------------------------------------------------- */
8207
-
8208
- namespace Predis;
8209
-
8210
- use Predis\Command\CommandInterface;
8211
- use Predis\Configuration\OptionsInterface;
8212
- use Predis\Connection\ConnectionInterface;
8213
- use Predis\Profile\ProfileInterface;
8214
- use Predis\Connection\NodeConnectionInterface;
8215
- use Predis\Command\RawCommand;
8216
- use Predis\Command\ScriptCommand;
8217
- use Predis\Configuration\Options;
8218
- use Predis\Connection\AggregateConnectionInterface;
8219
- use Predis\Connection\ParametersInterface;
8220
- use Predis\Monitor\Consumer as MonitorConsumer;
8221
- use Predis\Pipeline\Pipeline;
8222
- use Predis\PubSub\Consumer as PubSubConsumer;
8223
- use Predis\Response\ErrorInterface as ErrorResponseInterface;
8224
- use Predis\Response\ResponseInterface;
8225
- use Predis\Response\ServerException;
8226
- use Predis\Transaction\MultiExec as MultiExecTransaction;
8227
-
8228
- /**
8229
- * Base exception class for Predis-related errors.
8230
- *
8231
- * @author Daniele Alessandri <suppakilla@gmail.com>
8232
- */
8233
- abstract class PredisException extends \Exception
8234
- {
8235
- }
8236
-
8237
- /**
8238
- * Interface defining a client-side context such as a pipeline or transaction.
8239
- *
8240
- * @method $this del(array|string $keys)
8241
- * @method $this dump($key)
8242
- * @method $this exists($key)
8243
- * @method $this expire($key, $seconds)
8244
- * @method $this expireat($key, $timestamp)
8245
- * @method $this keys($pattern)
8246
- * @method $this move($key, $db)
8247
- * @method $this object($subcommand, $key)
8248
- * @method $this persist($key)
8249
- * @method $this pexpire($key, $milliseconds)
8250
- * @method $this pexpireat($key, $timestamp)
8251
- * @method $this pttl($key)
8252
- * @method $this randomkey()
8253
- * @method $this rename($key, $target)
8254
- * @method $this renamenx($key, $target)
8255
- * @method $this scan($cursor, array $options = null)
8256
- * @method $this sort($key, array $options = null)
8257
- * @method $this ttl($key)
8258
- * @method $this type($key)
8259
- * @method $this append($key, $value)
8260
- * @method $this bitcount($key, $start = null, $end = null)
8261
- * @method $this bitop($operation, $destkey, $key)
8262
- * @method $this bitfield($key, $subcommand, ...$subcommandArg)
8263
- * @method $this decr($key)
8264
- * @method $this decrby($key, $decrement)
8265
- * @method $this get($key)
8266
- * @method $this getbit($key, $offset)
8267
- * @method $this getrange($key, $start, $end)
8268
- * @method $this getset($key, $value)
8269
- * @method $this incr($key)
8270
- * @method $this incrby($key, $increment)
8271
- * @method $this incrbyfloat($key, $increment)
8272
- * @method $this mget(array $keys)
8273
- * @method $this mset(array $dictionary)
8274
- * @method $this msetnx(array $dictionary)
8275
- * @method $this psetex($key, $milliseconds, $value)
8276
- * @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
8277
- * @method $this setbit($key, $offset, $value)
8278
- * @method $this setex($key, $seconds, $value)
8279
- * @method $this setnx($key, $value)
8280
- * @method $this setrange($key, $offset, $value)
8281
- * @method $this strlen($key)
8282
- * @method $this hdel($key, array $fields)
8283
- * @method $this hexists($key, $field)
8284
- * @method $this hget($key, $field)
8285
- * @method $this hgetall($key)
8286
- * @method $this hincrby($key, $field, $increment)
8287
- * @method $this hincrbyfloat($key, $field, $increment)
8288
- * @method $this hkeys($key)
8289
- * @method $this hlen($key)
8290
- * @method $this hmget($key, array $fields)
8291
- * @method $this hmset($key, array $dictionary)
8292
- * @method $this hscan($key, $cursor, array $options = null)
8293
- * @method $this hset($key, $field, $value)
8294
- * @method $this hsetnx($key, $field, $value)
8295
- * @method $this hvals($key)
8296
- * @method $this hstrlen($key, $field)
8297
- * @method $this blpop(array|string $keys, $timeout)
8298
- * @method $this brpop(array|string $keys, $timeout)
8299
- * @method $this brpoplpush($source, $destination, $timeout)
8300
- * @method $this lindex($key, $index)
8301
- * @method $this linsert($key, $whence, $pivot, $value)
8302
- * @method $this llen($key)
8303
- * @method $this lpop($key)
8304
- * @method $this lpush($key, array $values)
8305
- * @method $this lpushx($key, $value)
8306
- * @method $this lrange($key, $start, $stop)
8307
- * @method $this lrem($key, $count, $value)
8308
- * @method $this lset($key, $index, $value)
8309
- * @method $this ltrim($key, $start, $stop)
8310
- * @method $this rpop($key)
8311
- * @method $this rpoplpush($source, $destination)
8312
- * @method $this rpush($key, array $values)
8313
- * @method $this rpushx($key, $value)
8314
- * @method $this sadd($key, array $members)
8315
- * @method $this scard($key)
8316
- * @method $this sdiff(array|string $keys)
8317
- * @method $this sdiffstore($destination, array|string $keys)
8318
- * @method $this sinter(array|string $keys)
8319
- * @method $this sinterstore($destination, array|string $keys)
8320
- * @method $this sismember($key, $member)
8321
- * @method $this smembers($key)
8322
- * @method $this smove($source, $destination, $member)
8323
- * @method $this spop($key, $count = null)
8324
- * @method $this srandmember($key, $count = null)
8325
- * @method $this srem($key, $member)
8326
- * @method $this sscan($key, $cursor, array $options = null)
8327
- * @method $this sunion(array|string $keys)
8328
- * @method $this sunionstore($destination, array|string $keys)
8329
- * @method $this zadd($key, array $membersAndScoresDictionary)
8330
- * @method $this zcard($key)
8331
- * @method $this zcount($key, $min, $max)
8332
- * @method $this zincrby($key, $increment, $member)
8333
- * @method $this zinterstore($destination, array|string $keys, array $options = null)
8334
- * @method $this zrange($key, $start, $stop, array $options = null)
8335
- * @method $this zrangebyscore($key, $min, $max, array $options = null)
8336
- * @method $this zrank($key, $member)
8337
- * @method $this zrem($key, $member)
8338
- * @method $this zremrangebyrank($key, $start, $stop)
8339
- * @method $this zremrangebyscore($key, $min, $max)
8340
- * @method $this zrevrange($key, $start, $stop, array $options = null)
8341
- * @method $this zrevrangebyscore($key, $min, $max, array $options = null)
8342
- * @method $this zrevrank($key, $member)
8343
- * @method $this zunionstore($destination, array|string $keys, array $options = null)
8344
- * @method $this zscore($key, $member)
8345
- * @method $this zscan($key, $cursor, array $options = null)
8346
- * @method $this zrangebylex($key, $start, $stop, array $options = null)
8347
- * @method $this zrevrangebylex($key, $start, $stop, array $options = null)
8348
- * @method $this zremrangebylex($key, $min, $max)
8349
- * @method $this zlexcount($key, $min, $max)
8350
- * @method $this pfadd($key, array $elements)
8351
- * @method $this pfmerge($destinationKey, array|string $sourceKeys)
8352
- * @method $this pfcount(array|string $keys)
8353
- * @method $this pubsub($subcommand, $argument)
8354
- * @method $this publish($channel, $message)
8355
- * @method $this discard()
8356
- * @method $this exec()
8357
- * @method $this multi()
8358
- * @method $this unwatch()
8359
- * @method $this watch($key)
8360
- * @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
8361
- * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
8362
- * @method $this script($subcommand, $argument = null)
8363
- * @method $this auth($password)
8364
- * @method $this echo($message)
8365
- * @method $this ping($message = null)
8366
- * @method $this select($database)
8367
- * @method $this bgrewriteaof()
8368
- * @method $this bgsave()
8369
- * @method $this client($subcommand, $argument = null)
8370
- * @method $this config($subcommand, $argument = null)
8371
- * @method $this dbsize()
8372
- * @method $this flushall()
8373
- * @method $this flushdb()
8374
- * @method $this info($section = null)
8375
- * @method $this lastsave()
8376
- * @method $this save()
8377
- * @method $this slaveof($host, $port)
8378
- * @method $this slowlog($subcommand, $argument = null)
8379
- * @method $this time()
8380
- * @method $this command()
8381
- * @method $this geoadd($key, $longitude, $latitude, $member)
8382
- * @method $this geohash($key, array $members)
8383
- * @method $this geopos($key, array $members)
8384
- * @method $this geodist($key, $member1, $member2, $unit = null)
8385
- * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
8386
- * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
8387
- *
8388
- * @author Daniele Alessandri <suppakilla@gmail.com>
8389
- */
8390
- interface ClientContextInterface
8391
- {
8392
- /**
8393
- * Sends the specified command instance to Redis.
8394
- *
8395
- * @param CommandInterface $command Command instance.
8396
- *
8397
- * @return mixed
8398
- */
8399
- public function executeCommand(CommandInterface $command);
8400
-
8401
- /**
8402
- * Sends the specified command with its arguments to Redis.
8403
- *
8404
- * @param string $method Command ID.
8405
- * @param array $arguments Arguments for the command.
8406
- *
8407
- * @return mixed
8408
- */
8409
- public function __call($method, $arguments);
8410
-
8411
- /**
8412
- * Starts the execution of the context.
8413
- *
8414
- * @param mixed $callable Optional callback for execution.
8415
- *
8416
- * @return array
8417
- */
8418
- public function execute($callable = null);
8419
- }
8420
-
8421
- /**
8422
- * Base exception class for network-related errors.
8423
- *
8424
- * @author Daniele Alessandri <suppakilla@gmail.com>
8425
- */
8426
- abstract class CommunicationException extends PredisException
8427
- {
8428
- private $connection;
8429
-
8430
- /**
8431
- * @param NodeConnectionInterface $connection Connection that generated the exception.
8432
- * @param string $message Error message.
8433
- * @param int $code Error code.
8434
- * @param \Exception $innerException Inner exception for wrapping the original error.
8435
- */
8436
- public function __construct(
8437
- NodeConnectionInterface $connection,
8438
- $message = null,
8439
- $code = null,
8440
- \Exception $innerException = null
8441
- ) {
8442
- parent::__construct($message, $code, $innerException);
8443
- $this->connection = $connection;
8444
- }
8445
-
8446
- /**
8447
- * Gets the connection that generated the exception.
8448
- *
8449
- * @return NodeConnectionInterface
8450
- */
8451
- public function getConnection()
8452
- {
8453
- return $this->connection;
8454
- }
8455
-
8456
- /**
8457
- * Indicates if the receiver should reset the underlying connection.
8458
- *
8459
- * @return bool
8460
- */
8461
- public function shouldResetConnection()
8462
- {
8463
- return true;
8464
- }
8465
-
8466
- /**
8467
- * Helper method to handle exceptions generated by a connection object.
8468
- *
8469
- * @param CommunicationException $exception Exception.
8470
- *
8471
- * @throws CommunicationException
8472
- */
8473
- public static function handle(CommunicationException $exception)
8474
- {
8475
- if ($exception->shouldResetConnection()) {
8476
- $connection = $exception->getConnection();
8477
-
8478
- if ($connection->isConnected()) {
8479
- $connection->disconnect();
8480
- }
8481
- }
8482
-
8483
- throw $exception;
8484
- }
8485
- }
8486
-
8487
- /**
8488
- * Exception class that identifies client-side errors.
8489
- *
8490
- * @author Daniele Alessandri <suppakilla@gmail.com>
8491
- */
8492
- class ClientException extends PredisException
8493
- {
8494
- }
8495
-
8496
- /**
8497
- * Interface defining a client able to execute commands against Redis.
8498
- *
8499
- * All the commands exposed by the client generally have the same signature as
8500
- * described by the Redis documentation, but some of them offer an additional
8501
- * and more friendly interface to ease programming which is described in the
8502
- * following list of methods:
8503
- *
8504
- * @method int del(array|string $keys)
8505
- * @method string dump($key)
8506
- * @method int exists($key)
8507
- * @method int expire($key, $seconds)
8508
- * @method int expireat($key, $timestamp)
8509
- * @method array keys($pattern)
8510
- * @method int move($key, $db)
8511
- * @method mixed object($subcommand, $key)
8512
- * @method int persist($key)
8513
- * @method int pexpire($key, $milliseconds)
8514
- * @method int pexpireat($key, $timestamp)
8515
- * @method int pttl($key)
8516
- * @method string randomkey()
8517
- * @method mixed rename($key, $target)
8518
- * @method int renamenx($key, $target)
8519
- * @method array scan($cursor, array $options = null)
8520
- * @method array sort($key, array $options = null)
8521
- * @method int ttl($key)
8522
- * @method mixed type($key)
8523
- * @method int append($key, $value)
8524
- * @method int bitcount($key, $start = null, $end = null)
8525
- * @method int bitop($operation, $destkey, $key)
8526
- * @method array bitfield($key, $subcommand, ...$subcommandArg)
8527
- * @method int decr($key)
8528
- * @method int decrby($key, $decrement)
8529
- * @method string get($key)
8530
- * @method int getbit($key, $offset)
8531
- * @method string getrange($key, $start, $end)
8532
- * @method string getset($key, $value)
8533
- * @method int incr($key)
8534
- * @method int incrby($key, $increment)
8535
- * @method string incrbyfloat($key, $increment)
8536
- * @method array mget(array $keys)
8537
- * @method mixed mset(array $dictionary)
8538
- * @method int msetnx(array $dictionary)
8539
- * @method mixed psetex($key, $milliseconds, $value)
8540
- * @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
8541
- * @method int setbit($key, $offset, $value)
8542
- * @method int setex($key, $seconds, $value)
8543
- * @method int setnx($key, $value)
8544
- * @method int setrange($key, $offset, $value)
8545
- * @method int strlen($key)
8546
- * @method int hdel($key, array $fields)
8547
- * @method int hexists($key, $field)
8548
- * @method string hget($key, $field)
8549
- * @method array hgetall($key)
8550
- * @method int hincrby($key, $field, $increment)
8551
- * @method string hincrbyfloat($key, $field, $increment)
8552
- * @method array hkeys($key)
8553
- * @method int hlen($key)
8554
- * @method array hmget($key, array $fields)
8555
- * @method mixed hmset($key, array $dictionary)
8556
- * @method array hscan($key, $cursor, array $options = null)
8557
- * @method int hset($key, $field, $value)
8558
- * @method int hsetnx($key, $field, $value)
8559
- * @method array hvals($key)
8560
- * @method int hstrlen($key, $field)
8561
- * @method array blpop(array|string $keys, $timeout)
8562
- * @method array brpop(array|string $keys, $timeout)
8563
- * @method array brpoplpush($source, $destination, $timeout)
8564
- * @method string lindex($key, $index)
8565
- * @method int linsert($key, $whence, $pivot, $value)
8566
- * @method int llen($key)
8567
- * @method string lpop($key)
8568
- * @method int lpush($key, array $values)
8569
- * @method int lpushx($key, $value)
8570
- * @method array lrange($key, $start, $stop)
8571
- * @method int lrem($key, $count, $value)
8572
- * @method mixed lset($key, $index, $value)
8573
- * @method mixed ltrim($key, $start, $stop)
8574
- * @method string rpop($key)
8575
- * @method string rpoplpush($source, $destination)
8576
- * @method int rpush($key, array $values)
8577
- * @method int rpushx($key, $value)
8578
- * @method int sadd($key, array $members)
8579
- * @method int scard($key)
8580
- * @method array sdiff(array|string $keys)
8581
- * @method int sdiffstore($destination, array|string $keys)
8582
- * @method array sinter(array|string $keys)
8583
- * @method int sinterstore($destination, array|string $keys)
8584
- * @method int sismember($key, $member)
8585
- * @method array smembers($key)
8586
- * @method int smove($source, $destination, $member)
8587
- * @method string spop($key, $count = null)
8588
- * @method string srandmember($key, $count = null)
8589
- * @method int srem($key, $member)
8590
- * @method array sscan($key, $cursor, array $options = null)
8591
- * @method array sunion(array|string $keys)
8592
- * @method int sunionstore($destination, array|string $keys)
8593
- * @method int zadd($key, array $membersAndScoresDictionary)
8594
- * @method int zcard($key)
8595
- * @method string zcount($key, $min, $max)
8596
- * @method string zincrby($key, $increment, $member)
8597
- * @method int zinterstore($destination, array|string $keys, array $options = null)
8598
- * @method array zrange($key, $start, $stop, array $options = null)
8599
- * @method array zrangebyscore($key, $min, $max, array $options = null)
8600
- * @method int zrank($key, $member)
8601
- * @method int zrem($key, $member)
8602
- * @method int zremrangebyrank($key, $start, $stop)
8603
- * @method int zremrangebyscore($key, $min, $max)
8604
- * @method array zrevrange($key, $start, $stop, array $options = null)
8605
- * @method array zrevrangebyscore($key, $max, $min, array $options = null)
8606
- * @method int zrevrank($key, $member)
8607
- * @method int zunionstore($destination, array|string $keys, array $options = null)
8608
- * @method string zscore($key, $member)
8609
- * @method array zscan($key, $cursor, array $options = null)
8610
- * @method array zrangebylex($key, $start, $stop, array $options = null)
8611
- * @method array zrevrangebylex($key, $start, $stop, array $options = null)
8612
- * @method int zremrangebylex($key, $min, $max)
8613
- * @method int zlexcount($key, $min, $max)
8614
- * @method int pfadd($key, array $elements)
8615
- * @method mixed pfmerge($destinationKey, array|string $sourceKeys)
8616
- * @method int pfcount(array|string $keys)
8617
- * @method mixed pubsub($subcommand, $argument)
8618
- * @method int publish($channel, $message)
8619
- * @method mixed discard()
8620
- * @method array exec()
8621
- * @method mixed multi()
8622
- * @method mixed unwatch()
8623
- * @method mixed watch($key)
8624
- * @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
8625
- * @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
8626
- * @method mixed script($subcommand, $argument = null)
8627
- * @method mixed auth($password)
8628
- * @method string echo($message)
8629
- * @method mixed ping($message = null)
8630
- * @method mixed select($database)
8631
- * @method mixed bgrewriteaof()
8632
- * @method mixed bgsave()
8633
- * @method mixed client($subcommand, $argument = null)
8634
- * @method mixed config($subcommand, $argument = null)
8635
- * @method int dbsize()
8636
- * @method mixed flushall()
8637
- * @method mixed flushdb()
8638
- * @method array info($section = null)
8639
- * @method int lastsave()
8640
- * @method mixed save()
8641
- * @method mixed slaveof($host, $port)
8642
- * @method mixed slowlog($subcommand, $argument = null)
8643
- * @method array time()
8644
- * @method array command()
8645
- * @method int geoadd($key, $longitude, $latitude, $member)
8646
- * @method array geohash($key, array $members)
8647
- * @method array geopos($key, array $members)
8648
- * @method string geodist($key, $member1, $member2, $unit = null)
8649
- * @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
8650
- * @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
8651
- *
8652
- * @author Daniele Alessandri <suppakilla@gmail.com>
8653
- */
8654
- interface ClientInterface
8655
- {
8656
- /**
8657
- * Returns the server profile used by the client.
8658
- *
8659
- * @return ProfileInterface
8660
- */
8661
- public function getProfile();
8662
-
8663
- /**
8664
- * Returns the client options specified upon initialization.
8665
- *
8666
- * @return OptionsInterface
8667
- */
8668
- public function getOptions();
8669
-
8670
- /**
8671
- * Opens the underlying connection to the server.
8672
- */
8673
- public function connect();
8674
-
8675
- /**
8676
- * Closes the underlying connection from the server.
8677
- */
8678
- public function disconnect();
8679
-
8680
- /**
8681
- * Returns the underlying connection instance.
8682
- *
8683
- * @return ConnectionInterface
8684
- */
8685
- public function getConnection();
8686
-
8687
- /**
8688
- * Creates a new instance of the specified Redis command.
8689
- *
8690
- * @param string $method Command ID.
8691
- * @param array $arguments Arguments for the command.
8692
- *
8693
- * @return CommandInterface
8694
- */
8695
- public function createCommand($method, $arguments = array());
8696
-
8697
- /**
8698
- * Executes the specified Redis command.
8699
- *
8700
- * @param CommandInterface $command Command instance.
8701
- *
8702
- * @return mixed
8703
- */
8704
- public function executeCommand(CommandInterface $command);
8705
-
8706
- /**
8707
- * Creates a Redis command with the specified arguments and sends a request
8708
- * to the server.
8709
- *
8710
- * @param string $method Command ID.
8711
- * @param array $arguments Arguments for the command.
8712
- *
8713
- * @return mixed
8714
- */
8715
- public function __call($method, $arguments);
8716
- }
8717
-
8718
- /**
8719
- * Exception class thrown when trying to use features not supported by certain
8720
- * classes or abstractions of Predis.
8721
- *
8722
- * @author Daniele Alessandri <suppakilla@gmail.com>
8723
- */
8724
- class NotSupportedException extends PredisException
8725
- {
8726
- }
8727
-
8728
- /**
8729
- * Implements a lightweight PSR-0 compliant autoloader for Predis.
8730
- *
8731
- * @author Eric Naeseth <eric@thumbtack.com>
8732
- * @author Daniele Alessandri <suppakilla@gmail.com>
8733
- */
8734
- class Autoloader
8735
- {
8736
- private $directory;
8737
- private $prefix;
8738
- private $prefixLength;
8739
-
8740
- /**
8741
- * @param string $baseDirectory Base directory where the source files are located.
8742
- */
8743
- public function __construct($baseDirectory = __DIR__)
8744
- {
8745
- $this->directory = $baseDirectory;
8746
- $this->prefix = __NAMESPACE__.'\\';
8747
- $this->prefixLength = strlen($this->prefix);
8748
- }
8749
-
8750
- /**
8751
- * Registers the autoloader class with the PHP SPL autoloader.
8752
- *
8753
- * @param bool $prepend Prepend the autoloader on the stack instead of appending it.
8754
- */
8755
- public static function register($prepend = false)
8756
- {
8757
- spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
8758
- }
8759
-
8760
- /**
8761
- * Loads a class from a file using its fully qualified name.
8762
- *
8763
- * @param string $className Fully qualified name of a class.
8764
- */
8765
- public function autoload($className)
8766
- {
8767
- if (0 === strpos($className, $this->prefix)) {
8768
- $parts = explode('\\', substr($className, $this->prefixLength));
8769
- $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
8770
-
8771
- if (is_file($filepath)) {
8772
- require $filepath;
8773
- }
8774
- }
8775
- }
8776
- }
8777
-
8778
- /**
8779
- * Client class used for connecting and executing commands on Redis.
8780
- *
8781
- * This is the main high-level abstraction of Predis upon which various other
8782
- * abstractions are built. Internally it aggregates various other classes each
8783
- * one with its own responsibility and scope.
8784
- *
8785
- * {@inheritdoc}
8786
- *
8787
- * @author Daniele Alessandri <suppakilla@gmail.com>
8788
- */
8789
- class Client implements ClientInterface, \IteratorAggregate
8790
- {
8791
- const VERSION = '1.1.2-dev';
8792
-
8793
- protected $connection;
8794
- protected $options;
8795
- private $profile;
8796
-
8797
- /**
8798
- * @param mixed $parameters Connection parameters for one or more servers.
8799
- * @param mixed $options Options to configure some behaviours of the client.
8800
- */
8801
- public function __construct($parameters = null, $options = null)
8802
- {
8803
- $this->options = $this->createOptions($options ?: array());
8804
- $this->connection = $this->createConnection($parameters ?: array());
8805
- $this->profile = $this->options->profile;
8806
- }
8807
-
8808
- /**
8809
- * Creates a new instance of Predis\Configuration\Options from different
8810
- * types of arguments or simply returns the passed argument if it is an
8811
- * instance of Predis\Configuration\OptionsInterface.
8812
- *
8813
- * @param mixed $options Client options.
8814
- *
8815
- * @throws \InvalidArgumentException
8816
- *
8817
- * @return OptionsInterface
8818
- */
8819
- protected function createOptions($options)
8820
- {
8821
- if (is_array($options)) {
8822
- return new Options($options);
8823
- }
8824
-
8825
- if ($options instanceof OptionsInterface) {
8826
- return $options;
8827
- }
8828
-
8829
- throw new \InvalidArgumentException('Invalid type for client options.');
8830
- }
8831
-
8832
- /**
8833
- * Creates single or aggregate connections from different types of arguments
8834
- * (string, array) or returns the passed argument if it is an instance of a
8835
- * class implementing Predis\Connection\ConnectionInterface.
8836
- *
8837
- * Accepted types for connection parameters are:
8838
- *
8839
- * - Instance of Predis\Connection\ConnectionInterface.
8840
- * - Instance of Predis\Connection\ParametersInterface.
8841
- * - Array
8842
- * - String
8843
- * - Callable
8844
- *
8845
- * @param mixed $parameters Connection parameters or connection instance.
8846
- *
8847
- * @throws \InvalidArgumentException
8848
- *
8849
- * @return ConnectionInterface
8850
- */
8851
- protected function createConnection($parameters)
8852
- {
8853
- if ($parameters instanceof ConnectionInterface) {
8854
- return $parameters;
8855
- }
8856
-
8857
- if ($parameters instanceof ParametersInterface || is_string($parameters)) {
8858
- return $this->options->connections->create($parameters);
8859
- }
8860
-
8861
- if (is_array($parameters)) {
8862
- if (!isset($parameters[0])) {
8863
- return $this->options->connections->create($parameters);
8864
- }
8865
-
8866
- $options = $this->options;
8867
-
8868
- if ($options->defined('aggregate')) {
8869
- $initializer = $this->getConnectionInitializerWrapper($options->aggregate);
8870
- $connection = $initializer($parameters, $options);
8871
- } elseif ($options->defined('replication')) {
8872
- $replication = $options->replication;
8873
-
8874
- if ($replication instanceof AggregateConnectionInterface) {
8875
- $connection = $replication;
8876
- $options->connections->aggregate($connection, $parameters);
8877
- } else {
8878
- $initializer = $this->getConnectionInitializerWrapper($replication);
8879
- $connection = $initializer($parameters, $options);
8880
- }
8881
- } else {
8882
- $connection = $options->cluster;
8883
- $options->connections->aggregate($connection, $parameters);
8884
- }
8885
-
8886
- return $connection;
8887
- }
8888
-
8889
- if (is_callable($parameters)) {
8890
- $initializer = $this->getConnectionInitializerWrapper($parameters);
8891
- $connection = $initializer($this->options);
8892
-
8893
- return $connection;
8894
- }
8895
-
8896
- throw new \InvalidArgumentException('Invalid type for connection parameters.');
8897
- }
8898
-
8899
- /**
8900
- * Wraps a callable to make sure that its returned value represents a valid
8901
- * connection type.
8902
- *
8903
- * @param mixed $callable
8904
- *
8905
- * @return \Closure
8906
- */
8907
- protected function getConnectionInitializerWrapper($callable)
8908
- {
8909
- return function () use ($callable) {
8910
- $connection = call_user_func_array($callable, func_get_args());
8911
-
8912
- if (!$connection instanceof ConnectionInterface) {
8913
- throw new \UnexpectedValueException(
8914
- 'The callable connection initializer returned an invalid type.'
8915
- );
8916
- }
8917
-
8918
- return $connection;
8919
- };
8920
- }
8921
-
8922
- /**
8923
- * {@inheritdoc}
8924
- */
8925
- public function getProfile()
8926
- {
8927
- return $this->profile;
8928
- }
8929
-
8930
- /**
8931
- * {@inheritdoc}
8932
- */
8933
- public function getOptions()
8934
- {
8935
- return $this->options;
8936
- }
8937
-
8938
- /**
8939
- * Creates a new client instance for the specified connection ID or alias,
8940
- * only when working with an aggregate connection (cluster, replication).
8941
- * The new client instances uses the same options of the original one.
8942
- *
8943
- * @param string $connectionID Identifier of a connection.
8944
- *
8945
- * @throws \InvalidArgumentException
8946
- *
8947
- * @return Client
8948
- */
8949
- public function getClientFor($connectionID)
8950
- {
8951
- if (!$connection = $this->getConnectionById($connectionID)) {
8952
- throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
8953
- }
8954
-
8955
- return new static($connection, $this->options);
8956
- }
8957
-
8958
- /**
8959
- * Opens the underlying connection and connects to the server.
8960
- */
8961
- public function connect()
8962
- {
8963
- $this->connection->connect();
8964
- }
8965
-
8966
- /**
8967
- * Closes the underlying connection and disconnects from the server.
8968
- */
8969
- public function disconnect()
8970
- {
8971
- $this->connection->disconnect();
8972
- }
8973
-
8974
- /**
8975
- * Closes the underlying connection and disconnects from the server.
8976
- *
8977
- * This is the same as `Client::disconnect()` as it does not actually send
8978
- * the `QUIT` command to Redis, but simply closes the connection.
8979
- */
8980
- public function quit()
8981
- {
8982
- $this->disconnect();
8983
- }
8984
-
8985
- /**
8986
- * Returns the current state of the underlying connection.
8987
- *
8988
- * @return bool
8989
- */
8990
- public function isConnected()
8991
- {
8992
- return $this->connection->isConnected();
8993
- }
8994
-
8995
- /**
8996
- * {@inheritdoc}
8997
- */
8998
- public function getConnection()
8999
- {
9000
- return $this->connection;
9001
- }
9002
-
9003
- /**
9004
- * Retrieves the specified connection from the aggregate connection when the
9005
- * client is in cluster or replication mode.
9006
- *
9007
- * @param string $connectionID Index or alias of the single connection.
9008
- *
9009
- * @throws NotSupportedException
9010
- *
9011
- * @return Connection\NodeConnectionInterface
9012
- */
9013
- public function getConnectionById($connectionID)
9014
- {
9015
- if (!$this->connection instanceof AggregateConnectionInterface) {
9016
- throw new NotSupportedException(
9017
- 'Retrieving connections by ID is supported only by aggregate connections.'
9018
- );
9019
- }
9020
-
9021
- return $this->connection->getConnectionById($connectionID);
9022
- }
9023
-
9024
- /**
9025
- * Executes a command without filtering its arguments, parsing the response,
9026
- * applying any prefix to keys or throwing exceptions on Redis errors even
9027
- * regardless of client options.
9028
- *
9029
- * It is possible to identify Redis error responses from normal responses
9030
- * using the second optional argument which is populated by reference.
9031
- *
9032
- * @param array $arguments Command arguments as defined by the command signature.
9033
- * @param bool $error Set to TRUE when Redis returned an error response.
9034
- *
9035
- * @return mixed
9036
- */
9037
- public function executeRaw(array $arguments, &$error = null)
9038
- {
9039
- $error = false;
9040
-
9041
- $response = $this->connection->executeCommand(
9042
- new RawCommand($arguments)
9043
- );
9044
-
9045
- if ($response instanceof ResponseInterface) {
9046
- if ($response instanceof ErrorResponseInterface) {
9047
- $error = true;
9048
- }
9049
-
9050
- return (string) $response;
9051
- }
9052
-
9053
- return $response;
9054
- }
9055
-
9056
- /**
9057
- * {@inheritdoc}
9058
- */
9059
- public function __call($commandID, $arguments)
9060
- {
9061
- return $this->executeCommand(
9062
- $this->createCommand($commandID, $arguments)
9063
- );
9064
- }
9065
-
9066
- /**
9067
- * {@inheritdoc}
9068
- */
9069
- public function createCommand($commandID, $arguments = array())
9070
- {
9071
- return $this->profile->createCommand($commandID, $arguments);
9072
- }
9073
-
9074
- /**
9075
- * {@inheritdoc}
9076
- */
9077
- public function executeCommand(CommandInterface $command)
9078
- {
9079
- $response = $this->connection->executeCommand($command);
9080
-
9081
- if ($response instanceof ResponseInterface) {
9082
- if ($response instanceof ErrorResponseInterface) {
9083
- $response = $this->onErrorResponse($command, $response);
9084
- }
9085
-
9086
- return $response;
9087
- }
9088
-
9089
- return $command->parseResponse($response);
9090
- }
9091
-
9092
- /**
9093
- * Handles -ERR responses returned by Redis.
9094
- *
9095
- * @param CommandInterface $command Redis command that generated the error.
9096
- * @param ErrorResponseInterface $response Instance of the error response.
9097
- *
9098
- * @throws ServerException
9099
- *
9100
- * @return mixed
9101
- */
9102
- protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
9103
- {
9104
- if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
9105
- $eval = $this->createCommand('EVAL');
9106
- $eval->setRawArguments($command->getEvalArguments());
9107
-
9108
- $response = $this->executeCommand($eval);
9109
-
9110
- if (!$response instanceof ResponseInterface) {
9111
- $response = $command->parseResponse($response);
9112
- }
9113
-
9114
- return $response;
9115
- }
9116
-
9117
- if ($this->options->exceptions) {
9118
- throw new ServerException($response->getMessage());
9119
- }
9120
-
9121
- return $response;
9122
- }
9123
-
9124
- /**
9125
- * Executes the specified initializer method on `$this` by adjusting the
9126
- * actual invokation depending on the arity (0, 1 or 2 arguments). This is
9127
- * simply an utility method to create Redis contexts instances since they
9128
- * follow a common initialization path.
9129
- *
9130
- * @param string $initializer Method name.
9131
- * @param array $argv Arguments for the method.
9132
- *
9133
- * @return mixed
9134
- */
9135
- private function sharedContextFactory($initializer, $argv = null)
9136
- {
9137
- switch (count($argv)) {
9138
- case 0:
9139
- return $this->$initializer();
9140
-
9141
- case 1:
9142
- return is_array($argv[0])
9143
- ? $this->$initializer($argv[0])
9144
- : $this->$initializer(null, $argv[0]);
9145
-
9146
- case 2:
9147
- list($arg0, $arg1) = $argv;
9148
-
9149
- return $this->$initializer($arg0, $arg1);
9150
-
9151
- default:
9152
- return $this->$initializer($this, $argv);
9153
- }
9154
- }
9155
-
9156
- /**
9157
- * Creates a new pipeline context and returns it, or returns the results of
9158
- * a pipeline executed inside the optionally provided callable object.
9159
- *
9160
- * @param mixed ... Array of options, a callable for execution, or both.
9161
- *
9162
- * @return Pipeline|array
9163
- */
9164
- public function pipeline(/* arguments */)
9165
- {
9166
- return $this->sharedContextFactory('createPipeline', func_get_args());
9167
- }
9168
-
9169
- /**
9170
- * Actual pipeline context initializer method.
9171
- *
9172
- * @param array $options Options for the context.
9173
- * @param mixed $callable Optional callable used to execute the context.
9174
- *
9175
- * @return Pipeline|array
9176
- */
9177
- protected function createPipeline(array $options = null, $callable = null)
9178
- {
9179
- if (isset($options['atomic']) && $options['atomic']) {
9180
- $class = 'Predis\Pipeline\Atomic';
9181
- } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
9182
- $class = 'Predis\Pipeline\FireAndForget';
9183
- } else {
9184
- $class = 'Predis\Pipeline\Pipeline';
9185
- }
9186
-
9187
- /*
9188
- * @var ClientContextInterface
9189
- */
9190
- $pipeline = new $class($this);
9191
-
9192
- if (isset($callable)) {
9193
- return $pipeline->execute($callable);
9194
- }
9195
-
9196
- return $pipeline;
9197
- }
9198
-
9199
- /**
9200
- * Creates a new transaction context and returns it, or returns the results
9201
- * of a transaction executed inside the optionally provided callable object.
9202
- *
9203
- * @param mixed ... Array of options, a callable for execution, or both.
9204
- *
9205
- * @return MultiExecTransaction|array
9206
- */
9207
- public function transaction(/* arguments */)
9208
- {
9209
- return $this->sharedContextFactory('createTransaction', func_get_args());
9210
- }
9211
-
9212
- /**
9213
- * Actual transaction context initializer method.
9214
- *
9215
- * @param array $options Options for the context.
9216
- * @param mixed $callable Optional callable used to execute the context.
9217
- *
9218
- * @return MultiExecTransaction|array
9219
- */
9220
- protected function createTransaction(array $options = null, $callable = null)
9221
- {
9222
- $transaction = new MultiExecTransaction($this, $options);
9223
-
9224
- if (isset($callable)) {
9225
- return $transaction->execute($callable);
9226
- }
9227
-
9228
- return $transaction;
9229
- }
9230
-
9231
- /**
9232
- * Creates a new publish/subscribe context and returns it, or starts its loop
9233
- * inside the optionally provided callable object.
9234
- *
9235
- * @param mixed ... Array of options, a callable for execution, or both.
9236
- *
9237
- * @return PubSubConsumer|null
9238
- */
9239
- public function pubSubLoop(/* arguments */)
9240
- {
9241
- return $this->sharedContextFactory('createPubSub', func_get_args());
9242
- }
9243
-
9244
- /**
9245
- * Actual publish/subscribe context initializer method.
9246
- *
9247
- * @param array $options Options for the context.
9248
- * @param mixed $callable Optional callable used to execute the context.
9249
- *
9250
- * @return PubSubConsumer|null
9251
- */
9252
- protected function createPubSub(array $options = null, $callable = null)
9253
- {
9254
- $pubsub = new PubSubConsumer($this, $options);
9255
-
9256
- if (!isset($callable)) {
9257
- return $pubsub;
9258
- }
9259
-
9260
- foreach ($pubsub as $message) {
9261
- if (call_user_func($callable, $pubsub, $message) === false) {
9262
- $pubsub->stop();
9263
- }
9264
- }
9265
- }
9266
-
9267
- /**
9268
- * Creates a new monitor consumer and returns it.
9269
- *
9270
- * @return MonitorConsumer
9271
- */
9272
- public function monitor()
9273
- {
9274
- return new MonitorConsumer($this);
9275
- }
9276
-
9277
- /**
9278
- * {@inheritdoc}
9279
- */
9280
- public function getIterator()
9281
- {
9282
- $clients = array();
9283
- $connection = $this->getConnection();
9284
-
9285
- if (!$connection instanceof \Traversable) {
9286
- throw new ClientException('The underlying connection is not traversable');
9287
- }
9288
-
9289
- foreach ($connection as $node) {
9290
- $clients[(string) $node] = new static($node, $this->getOptions());
9291
- }
9292
-
9293
- return new \ArrayIterator($clients);
9294
- }
9295
- }
9296
-
9297
- /* --------------------------------------------------------------------------- */
9298
-
9299
- namespace Predis\Configuration;
9300
-
9301
- use Predis\Command\Processor\KeyPrefixProcessor;
9302
- use Predis\Command\Processor\ProcessorInterface;
9303
- use Predis\Profile\Factory;
9304
- use Predis\Profile\ProfileInterface;
9305
- use Predis\Profile\RedisProfile;
9306
- use Predis\Connection\Aggregate\ClusterInterface;
9307
- use Predis\Connection\Aggregate\ReplicationInterface;
9308
- use Predis\Connection\FactoryInterface;
9309
- use Predis\Connection\Aggregate\PredisCluster;
9310
- use Predis\Connection\Aggregate\RedisCluster;
9311
- use Predis\Connection\Aggregate\MasterSlaveReplication;
9312
- use Predis\Connection\Aggregate\SentinelReplication;
9313
- use Predis\Connection\Factory as Predis_Factory;
9314
-
9315
- /**
9316
- * Defines an handler used by Predis\Configuration\Options to filter, validate
9317
- * or return default values for a given option.
9318
- *
9319
- * @author Daniele Alessandri <suppakilla@gmail.com>
9320
- */
9321
- interface OptionInterface
9322
- {
9323
- /**
9324
- * Filters and validates the passed value.
9325
- *
9326
- * @param OptionsInterface $options Options container.
9327
- * @param mixed $value Input value.
9328
- *
9329
- * @return mixed
9330
- */
9331
- public function filter(OptionsInterface $options, $value);
9332
-
9333
- /**
9334
- * Returns the default value for the option.
9335
- *
9336
- * @param OptionsInterface $options Options container.
9337
- *
9338
- * @return mixed
9339
- */
9340
- public function getDefault(OptionsInterface $options);
9341
- }
9342
-
9343
- /**
9344
- * Interface defining a container for client options.
9345
- *
9346
- * @property-read callable $aggregate Custom connection aggregator.
9347
- * @property-read ClusterInterface $cluster Aggregate connection for clustering.
9348
- * @property-read FactoryInterface $connections Connection factory.
9349
- * @property-read bool $exceptions Toggles exceptions in client for -ERR responses.
9350
- * @property-read ProcessorInterface $prefix Key prefixing strategy using the given prefix.
9351
- * @property-read ProfileInterface $profile Server profile.
9352
- * @property-read ReplicationInterface $replication Aggregate connection for replication.
9353
- *
9354
- * @author Daniele Alessandri <suppakilla@gmail.com>
9355
- */
9356
- interface OptionsInterface
9357
- {
9358
- /**
9359
- * Returns the default value for the given option.
9360
- *
9361
- * @param string $option Name of the option.
9362
- *
9363
- * @return mixed|null
9364
- */
9365
- public function getDefault($option);
9366
-
9367
- /**
9368
- * Checks if the given option has been set by the user upon initialization.
9369
- *
9370
- * @param string $option Name of the option.
9371
- *
9372
- * @return bool
9373
- */
9374
- public function defined($option);
9375
-
9376
- /**
9377
- * Checks if the given option has been set and does not evaluate to NULL.
9378
- *
9379
- * @param string $option Name of the option.
9380
- *
9381
- * @return bool
9382
- */
9383
- public function __isset($option);
9384
-
9385
- /**
9386
- * Returns the value of the given option.
9387
- *
9388
- * @param string $option Name of the option.
9389
- *
9390
- * @return mixed|null
9391
- */
9392
- public function __get($option);
9393
- }
9394
-
9395
- /**
9396
- * Configures a command processor that apply the specified prefix string to a
9397
- * series of Redis commands considered prefixable.
9398
- *
9399
- * @author Daniele Alessandri <suppakilla@gmail.com>
9400
- */
9401
- class PrefixOption implements OptionInterface
9402
- {
9403
- /**
9404
- * {@inheritdoc}
9405
- */
9406
- public function filter(OptionsInterface $options, $value)
9407
- {
9408
- if ($value instanceof ProcessorInterface) {
9409
- return $value;
9410
- }
9411
-
9412
- return new KeyPrefixProcessor($value);
9413
- }
9414
-
9415
- /**
9416
- * {@inheritdoc}
9417
- */
9418
- public function getDefault(OptionsInterface $options)
9419
- {
9420
- // NOOP
9421
- }
9422
- }
9423
-
9424
- /**
9425
- * Configures whether consumers (such as the client) should throw exceptions on
9426
- * Redis errors (-ERR responses) or just return instances of error responses.
9427
- *
9428
- * @author Daniele Alessandri <suppakilla@gmail.com>
9429
- */
9430
- class ExceptionsOption implements OptionInterface
9431
- {
9432
- /**
9433
- * {@inheritdoc}
9434
- */
9435
- public function filter(OptionsInterface $options, $value)
9436
- {
9437
- return filter_var($value, FILTER_VALIDATE_BOOLEAN);
9438
- }
9439
-
9440
- /**
9441
- * {@inheritdoc}
9442
- */
9443
- public function getDefault(OptionsInterface $options)
9444
- {
9445
- return true;
9446
- }
9447
- }
9448
-
9449
- /**
9450
- * Configures the server profile to be used by the client to create command
9451
- * instances depending on the specified version of the Redis server.
9452
- *
9453
- * @author Daniele Alessandri <suppakilla@gmail.com>
9454
- */
9455
- class ProfileOption implements OptionInterface
9456
- {
9457
- /**
9458
- * Sets the commands processors that need to be applied to the profile.
9459
- *
9460
- * @param OptionsInterface $options Client options.
9461
- * @param ProfileInterface $profile Server profile.
9462
- */
9463
- protected function setProcessors(OptionsInterface $options, ProfileInterface $profile)
9464
- {
9465
- if (isset($options->prefix) && $profile instanceof RedisProfile) {
9466
- // NOTE: directly using __get('prefix') is actually a workaround for
9467
- // HHVM 2.3.0. It's correct and respects the options interface, it's
9468
- // just ugly. We will remove this hack when HHVM will fix re-entrant
9469
- // calls to __get() once and for all.
9470
-
9471
- $profile->setProcessor($options->__get('prefix'));
9472
- }
9473
- }
9474
-
9475
- /**
9476
- * {@inheritdoc}
9477
- */
9478
- public function filter(OptionsInterface $options, $value)
9479
- {
9480
- if (is_string($value)) {
9481
- $value = Factory::get($value);
9482
- $this->setProcessors($options, $value);
9483
- } elseif (!$value instanceof ProfileInterface) {
9484
- throw new \InvalidArgumentException('Invalid value for the profile option.');
9485
- }
9486
-
9487
- return $value;
9488
- }
9489
-
9490
- /**
9491
- * {@inheritdoc}
9492
- */
9493
- public function getDefault(OptionsInterface $options)
9494
- {
9495
- $profile = Factory::getDefault();
9496
- $this->setProcessors($options, $profile);
9497
-
9498
- return $profile;
9499
- }
9500
- }
9501
-
9502
- /**
9503
- * Configures an aggregate connection used for clustering
9504
- * multiple Redis nodes using various implementations with
9505
- * different algorithms or strategies.
9506
- *
9507
- * @author Daniele Alessandri <suppakilla@gmail.com>
9508
- */
9509
- class ClusterOption implements OptionInterface
9510
- {
9511
- /**
9512
- * Creates a new cluster connection from on a known descriptive name.
9513
- *
9514
- * @param OptionsInterface $options Instance of the client options.
9515
- * @param string $id Descriptive identifier of the cluster type (`predis`, `redis-cluster`)
9516
- *
9517
- * @return ClusterInterface|null
9518
- */
9519
- protected function createByDescription(OptionsInterface $options, $id)
9520
- {
9521
- switch ($id) {
9522
- case 'predis':
9523
- case 'predis-cluster':
9524
- return new PredisCluster();
9525
-
9526
- case 'redis':
9527
- case 'redis-cluster':
9528
- return new RedisCluster($options->connections);
9529
-
9530
- default:
9531
- return;
9532
- }
9533
- }
9534
-
9535
- /**
9536
- * {@inheritdoc}
9537
- */
9538
- public function filter(OptionsInterface $options, $value)
9539
- {
9540
- if (is_string($value)) {
9541
- $value = $this->createByDescription($options, $value);
9542
- }
9543
-
9544
- if (!$value instanceof ClusterInterface) {
9545
- throw new \InvalidArgumentException(
9546
- "An instance of type 'Predis\Connection\Aggregate\ClusterInterface' was expected."
9547
- );
9548
- }
9549
-
9550
- return $value;
9551
- }
9552
-
9553
- /**
9554
- * {@inheritdoc}
9555
- */
9556
- public function getDefault(OptionsInterface $options)
9557
- {
9558
- return new PredisCluster();
9559
- }
9560
- }
9561
-
9562
- /**
9563
- * Manages Predis options with filtering, conversion and lazy initialization of
9564
- * values using a mini-DI container approach.
9565
- *
9566
- * {@inheritdoc}
9567
- *
9568
- * @author Daniele Alessandri <suppakilla@gmail.com>
9569
- */
9570
- class Options implements OptionsInterface
9571
- {
9572
- protected $input;
9573
- protected $options;
9574
- protected $handlers;
9575
-
9576
- /**
9577
- * @param array $options Array of options with their values
9578
- */
9579
- public function __construct(array $options = array())
9580
- {
9581
- $this->input = $options;
9582
- $this->options = array();
9583
- $this->handlers = $this->getHandlers();
9584
- }
9585
-
9586
- /**
9587
- * Ensures that the default options are initialized.
9588
- *
9589
- * @return array
9590
- */
9591
- protected function getHandlers()
9592
- {
9593
- return array(
9594
- 'cluster' => 'Predis\Configuration\ClusterOption',
9595
- 'connections' => 'Predis\Configuration\ConnectionFactoryOption',
9596
- 'exceptions' => 'Predis\Configuration\ExceptionsOption',
9597
- 'prefix' => 'Predis\Configuration\PrefixOption',
9598
- 'profile' => 'Predis\Configuration\ProfileOption',
9599
- 'replication' => 'Predis\Configuration\ReplicationOption',
9600
- );
9601
- }
9602
-
9603
- /**
9604
- * {@inheritdoc}
9605
- */
9606
- public function getDefault($option)
9607
- {
9608
- if (isset($this->handlers[$option])) {
9609
- $handler = $this->handlers[$option];
9610
- $handler = new $handler();
9611
-
9612
- return $handler->getDefault($this);
9613
- }
9614
- }
9615
-
9616
- /**
9617
- * {@inheritdoc}
9618
- */
9619
- public function defined($option)
9620
- {
9621
- return
9622
- array_key_exists($option, $this->options) ||
9623
- array_key_exists($option, $this->input)
9624
- ;
9625
- }
9626
-
9627
- /**
9628
- * {@inheritdoc}
9629
- */
9630
- public function __isset($option)
9631
- {
9632
- return (
9633
- array_key_exists($option, $this->options) ||
9634
- array_key_exists($option, $this->input)
9635
- ) && $this->__get($option) !== null;
9636
- }
9637
-
9638
- /**
9639
- * {@inheritdoc}
9640
- */
9641
- public function __get($option)
9642
- {
9643
- if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
9644
- return $this->options[$option];
9645
- }
9646
-
9647
- if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
9648
- $value = $this->input[$option];
9649
- unset($this->input[$option]);
9650
-
9651
- if (is_object($value) && method_exists($value, '__invoke')) {
9652
- $value = $value($this, $option);
9653
- }
9654
-
9655
- if (isset($this->handlers[$option])) {
9656
- $handler = $this->handlers[$option];
9657
- $handler = new $handler();
9658
- $value = $handler->filter($this, $value);
9659
- }
9660
-
9661
- return $this->options[$option] = $value;
9662
- }
9663
-
9664
- if (isset($this->handlers[$option])) {
9665
- return $this->options[$option] = $this->getDefault($option);
9666
- }
9667
-
9668
- return;
9669
- }
9670
- }
9671
-
9672
- /**
9673
- * Configures an aggregate connection used for master/slave replication among
9674
- * multiple Redis nodes.
9675
- *
9676
- * @author Daniele Alessandri <suppakilla@gmail.com>
9677
- */
9678
- class ReplicationOption implements OptionInterface
9679
- {
9680
- /**
9681
- * {@inheritdoc}
9682
- *
9683
- * @todo There's more code than needed due to a bug in filter_var() as
9684
- * discussed here https://bugs.php.net/bug.php?id=49510 and different
9685
- * behaviours when encountering NULL values on PHP 5.3.
9686
- */
9687
- public function filter(OptionsInterface $options, $value)
9688
- {
9689
- if ($value instanceof ReplicationInterface) {
9690
- return $value;
9691
- }
9692
-
9693
- if (is_bool($value) || $value === null) {
9694
- return $value ? $this->getDefault($options) : null;
9695
- }
9696
-
9697
- if ($value === 'sentinel') {
9698
- return function ($sentinels, $options) {
9699
- return new SentinelReplication($options->service, $sentinels, $options->connections);
9700
- };
9701
- }
9702
-
9703
- if (
9704
- !is_object($value) &&
9705
- null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
9706
- ) {
9707
- return $asbool ? $this->getDefault($options) : null;
9708
- }
9709
-
9710
- throw new \InvalidArgumentException(
9711
- "An instance of type 'Predis\Connection\Aggregate\ReplicationInterface' was expected."
9712
- );
9713
- }
9714
-
9715
- /**
9716
- * {@inheritdoc}
9717
- */
9718
- public function getDefault(OptionsInterface $options)
9719
- {
9720
- $replication = new MasterSlaveReplication();
9721
-
9722
- if ($options->autodiscovery) {
9723
- $replication->setConnectionFactory($options->connections);
9724
- $replication->setAutoDiscovery(true);
9725
- }
9726
-
9727
- return $replication;
9728
- }
9729
- }
9730
-
9731
- /**
9732
- * Configures a connection factory used by the client to create new connection
9733
- * instances for single Redis nodes.
9734
- *
9735
- * @author Daniele Alessandri <suppakilla@gmail.com>
9736
- */
9737
- class ConnectionPredis_FactoryOption implements OptionInterface
9738
- {
9739
- /**
9740
- * {@inheritdoc}
9741
- */
9742
- public function filter(OptionsInterface $options, $value)
9743
- {
9744
- if ($value instanceof Predis_FactoryInterface) {
9745
- return $value;
9746
- } elseif (is_array($value)) {
9747
- $factory = $this->getDefault($options);
9748
-
9749
- foreach ($value as $scheme => $initializer) {
9750
- $factory->define($scheme, $initializer);
9751
- }
9752
-
9753
- return $factory;
9754
- } else {
9755
- throw new \InvalidArgumentException(
9756
- 'Invalid value provided for the connections option.'
9757
- );
9758
- }
9759
- }
9760
-
9761
- /**
9762
- * {@inheritdoc}
9763
- */
9764
- public function getDefault(OptionsInterface $options)
9765
- {
9766
- $factory = new Predis_Factory();
9767
-
9768
- if ($options->defined('parameters')) {
9769
- $factory->setDefaultParameters($options->parameters);
9770
- }
9771
-
9772
- return $factory;
9773
- }
9774
- }
9775
-
9776
- /* --------------------------------------------------------------------------- */
9777
-
9778
- namespace Predis\Response;
9779
-
9780
- use Predis\PredisException;
9781
-
9782
- /**
9783
- * Represents a complex response object from Redis.
9784
- *
9785
- * @author Daniele Alessandri <suppakilla@gmail.com>
9786
- */
9787
- interface ResponseInterface
9788
- {
9789
- }
9790
-
9791
- /**
9792
- * Represents an error returned by Redis (responses identified by "-" in the
9793
- * Redis protocol) during the execution of an operation on the server.
9794
- *
9795
- * @author Daniele Alessandri <suppakilla@gmail.com>
9796
- */
9797
- interface ErrorInterface extends ResponseInterface
9798
- {
9799
- /**
9800
- * Returns the error message.
9801
- *
9802
- * @return string
9803
- */
9804
- public function getMessage();
9805
-
9806
- /**
9807
- * Returns the error type (e.g. ERR, ASK, MOVED).
9808
- *
9809
- * @return string
9810
- */
9811
- public function getErrorType();
9812
- }
9813
-
9814
- /**
9815
- * Exception class that identifies server-side Redis errors.
9816
- *
9817
- * @author Daniele Alessandri <suppakilla@gmail.com>
9818
- */
9819
- class ServerException extends PredisException implements ErrorInterface
9820
- {
9821
- /**
9822
- * Gets the type of the error returned by Redis.
9823
- *
9824
- * @return string
9825
- */
9826
- public function getErrorType()
9827
- {
9828
- list($errorType) = explode(' ', $this->getMessage(), 2);
9829
-
9830
- return $errorType;
9831
- }
9832
-
9833
- /**
9834
- * Converts the exception to an instance of Predis\Response\Error.
9835
- *
9836
- * @return Error
9837
- */
9838
- public function toErrorResponse()
9839
- {
9840
- return new Error($this->getMessage());
9841
- }
9842
- }
9843
-
9844
- /**
9845
- * Represents an error returned by Redis (-ERR responses) during the execution
9846
- * of a command on the server.
9847
- *
9848
- * @author Daniele Alessandri <suppakilla@gmail.com>
9849
- */
9850
- class Error implements ErrorInterface
9851
- {
9852
- private $message;
9853
-
9854
- /**
9855
- * @param string $message Error message returned by Redis
9856
- */
9857
- public function __construct($message)
9858
- {
9859
- $this->message = $message;
9860
- }
9861
-
9862
- /**
9863
- * {@inheritdoc}
9864
- */
9865
- public function getMessage()
9866
- {
9867
- return $this->message;
9868
- }
9869
-
9870
- /**
9871
- * {@inheritdoc}
9872
- */
9873
- public function getErrorType()
9874
- {
9875
- list($errorType) = explode(' ', $this->getMessage(), 2);
9876
-
9877
- return $errorType;
9878
- }
9879
-
9880
- /**
9881
- * Converts the object to its string representation.
9882
- *
9883
- * @return string
9884
- */
9885
- public function __toString()
9886
- {
9887
- return $this->getMessage();
9888
- }
9889
- }
9890
-
9891
- /**
9892
- * Represents a status response returned by Redis.
9893
- *
9894
- * @author Daniele Alessandri <suppakilla@gmail.com>
9895
- */
9896
- class Status implements ResponseInterface
9897
- {
9898
- private static $OK;
9899
- private static $QUEUED;
9900
-
9901
- private $payload;
9902
-
9903
- /**
9904
- * @param string $payload Payload of the status response as returned by Redis.
9905
- */
9906
- public function __construct($payload)
9907
- {
9908
- $this->payload = $payload;
9909
- }
9910
-
9911
- /**
9912
- * Converts the response object to its string representation.
9913
- *
9914
- * @return string
9915
- */
9916
- public function __toString()
9917
- {
9918
- return $this->payload;
9919
- }
9920
-
9921
- /**
9922
- * Returns the payload of status response.
9923
- *
9924
- * @return string
9925
- */
9926
- public function getPayload()
9927
- {
9928
- return $this->payload;
9929
- }
9930
-
9931
- /**
9932
- * Returns an instance of a status response object.
9933
- *
9934
- * Common status responses such as OK or QUEUED are cached in order to lower
9935
- * the global memory usage especially when using pipelines.
9936
- *
9937
- * @param string $payload Status response payload.
9938
- *
9939
- * @return string
9940
- */
9941
- public static function get($payload)
9942
- {
9943
- switch ($payload) {
9944
- case 'OK':
9945
- case 'QUEUED':
9946
- if (isset(self::$$payload)) {
9947
- return self::$$payload;
9948
- }
9949
-
9950
- return self::$$payload = new self($payload);
9951
-
9952
- default:
9953
- return new self($payload);
9954
- }
9955
- }
9956
- }
9957
-
9958
- /* --------------------------------------------------------------------------- */
9959
-
9960
- namespace Predis\Protocol\Text\Handler;
9961
-
9962
- use Predis\CommunicationException;
9963
- use Predis\Connection\CompositeConnectionInterface;
9964
- use Predis\Protocol\ProtocolException;
9965
- use Predis\Response\Status;
9966
- use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;
9967
- use Predis\Response\Error;
9968
-
9969
- /**
9970
- * Defines a pluggable handler used to parse a particular type of response.
9971
- *
9972
- * @author Daniele Alessandri <suppakilla@gmail.com>
9973
- */
9974
- interface ResponseHandlerInterface
9975
- {
9976
- /**
9977
- * Deserializes a response returned by Redis and reads more data from the
9978
- * connection if needed.
9979
- *
9980
- * @param CompositeConnectionInterface $connection Redis connection.
9981
- * @param string $payload String payload.
9982
- *
9983
- * @return mixed
9984
- */
9985
- public function handle(CompositeConnectionInterface $connection, $payload);
9986
- }
9987
-
9988
- /**
9989
- * Handler for the integer response type in the standard Redis wire protocol.
9990
- * It translates the payload an integer or NULL.
9991
- *
9992
- * @link http://redis.io/topics/protocol
9993
- *
9994
- * @author Daniele Alessandri <suppakilla@gmail.com>
9995
- */
9996
- class IntegerResponse implements ResponseHandlerInterface
9997
- {
9998
- /**
9999
- * {@inheritdoc}
10000
- */
10001
- public function handle(CompositeConnectionInterface $connection, $payload)
10002
- {
10003
- if (is_numeric($payload)) {
10004
- $integer = (int) $payload;
10005
- return $integer == $payload ? $integer : $payload;
10006
- }
10007
-
10008
- if ($payload !== 'nil') {
10009
- CommunicationException::handle(new ProtocolException(
10010
- $connection, "Cannot parse '$payload' as a valid numeric response."
10011
- ));
10012
- }
10013
-
10014
- return;
10015
- }
10016
- }
10017
-
10018
- /**
10019
- * Handler for the status response type in the standard Redis wire protocol. It
10020
- * translates certain classes of status response to PHP objects or just returns
10021
- * the payload as a string.
10022
- *
10023
- * @link http://redis.io/topics/protocol
10024
- *
10025
- * @author Daniele Alessandri <suppakilla@gmail.com>
10026
- */
10027
- class StatusResponse implements ResponseHandlerInterface
10028
- {
10029
- /**
10030
- * {@inheritdoc}
10031
- */
10032
- public function handle(CompositeConnectionInterface $connection, $payload)
10033
- {
10034
- return Status::get($payload);
10035
- }
10036
- }
10037
-
10038
- /**
10039
- * Handler for the bulk response type in the standard Redis wire protocol.
10040
- * It translates the payload to a string or a NULL.
10041
- *
10042
- * @link http://redis.io/topics/protocol
10043
- *
10044
- * @author Daniele Alessandri <suppakilla@gmail.com>
10045
- */
10046
- class BulkResponse implements ResponseHandlerInterface
10047
- {
10048
- /**
10049
- * {@inheritdoc}
10050
- */
10051
- public function handle(CompositeConnectionInterface $connection, $payload)
10052
- {
10053
- $length = (int) $payload;
10054
-
10055
- if ("$length" !== $payload) {
10056
- CommunicationException::handle(new ProtocolException(
10057
- $connection, "Cannot parse '$payload' as a valid length for a bulk response."
10058
- ));
10059
- }
10060
-
10061
- if ($length >= 0) {
10062
- return substr($connection->readBuffer($length + 2), 0, -2);
10063
- }
10064
-
10065
- if ($length == -1) {
10066
- return;
10067
- }
10068
-
10069
- CommunicationException::handle(new ProtocolException(
10070
- $connection, "Value '$payload' is not a valid length for a bulk response."
10071
- ));
10072
-
10073
- return;
10074
- }
10075
- }
10076
-
10077
- /**
10078
- * Handler for the multibulk response type in the standard Redis wire protocol.
10079
- * It returns multibulk responses as PHP arrays.
10080
- *
10081
- * @link http://redis.io/topics/protocol
10082
- *
10083
- * @author Daniele Alessandri <suppakilla@gmail.com>
10084
- */
10085
- class MultiBulkResponse implements ResponseHandlerInterface
10086
- {
10087
- /**
10088
- * {@inheritdoc}
10089
- */
10090
- public function handle(CompositeConnectionInterface $connection, $payload)
10091
- {
10092
- $length = (int) $payload;
10093
-
10094
- if ("$length" !== $payload) {
10095
- CommunicationException::handle(new ProtocolException(
10096
- $connection, "Cannot parse '$payload' as a valid length of a multi-bulk response."
10097
- ));
10098
- }
10099
-
10100
- if ($length === -1) {
10101
- return;
10102
- }
10103
-
10104
- $list = array();
10105
-
10106
- if ($length > 0) {
10107
- $handlersCache = array();
10108
- $reader = $connection->getProtocol()->getResponseReader();
10109
-
10110
- for ($i = 0; $i < $length; ++$i) {
10111
- $header = $connection->readLine();
10112
- $prefix = $header[0];
10113
-
10114
- if (isset($handlersCache[$prefix])) {
10115
- $handler = $handlersCache[$prefix];
10116
- } else {
10117
- $handler = $reader->getHandler($prefix);
10118
- $handlersCache[$prefix] = $handler;
10119
- }
10120
-
10121
- $list[$i] = $handler->handle($connection, substr($header, 1));
10122
- }
10123
- }
10124
-
10125
- return $list;
10126
- }
10127
- }
10128
-
10129
- /**
10130
- * Handler for the multibulk response type in the standard Redis wire protocol.
10131
- * It returns multibulk responses as iterators that can stream bulk elements.
10132
- *
10133
- * Streamable multibulk responses are not globally supported by the abstractions
10134
- * built-in into Predis, such as transactions or pipelines. Use them with care!
10135
- *
10136
- * @link http://redis.io/topics/protocol
10137
- *
10138
- * @author Daniele Alessandri <suppakilla@gmail.com>
10139
- */
10140
- class StreamableMultiBulkResponse implements ResponseHandlerInterface
10141
- {
10142
- /**
10143
- * {@inheritdoc}
10144
- */
10145
- public function handle(CompositeConnectionInterface $connection, $payload)
10146
- {
10147
- $length = (int) $payload;
10148
-
10149
- if ("$length" != $payload) {
10150
- CommunicationException::handle(new ProtocolException(
10151
- $connection, "Cannot parse '$payload' as a valid length for a multi-bulk response."
10152
- ));
10153
- }
10154
-
10155
- return new MultiBulkIterator($connection, $length);
10156
- }
10157
- }
10158
-
10159
- /**
10160
- * Handler for the error response type in the standard Redis wire protocol.
10161
- * It translates the payload to a complex response object for Predis.
10162
- *
10163
- * @link http://redis.io/topics/protocol
10164
- *
10165
- * @author Daniele Alessandri <suppakilla@gmail.com>
10166
- */
10167
- class ErrorResponse implements ResponseHandlerInterface
10168
- {
10169
- /**
10170
- * {@inheritdoc}
10171
- */
10172
- public function handle(CompositeConnectionInterface $connection, $payload)
10173
- {
10174
- return new Error($payload);
10175
- }
10176
- }
10177
-
10178
- /* --------------------------------------------------------------------------- */
10179
-
10180
- namespace Predis\Connection\Aggregate;
10181
-
10182
- use Predis\Connection\AggregateConnectionInterface;
10183
- use Predis\Connection\NodeConnectionInterface;
10184
- use Predis\ClientException;
10185
- use Predis\Command\CommandInterface;
10186
- use Predis\Command\RawCommand;
10187
- use Predis\Connection\ConnectionException;
10188
- use Predis\Connection\FactoryInterface;
10189
- use Predis\Replication\MissingMasterException;
10190
- use Predis\Replication\ReplicationStrategy;
10191
- use Predis\Response\ErrorInterface as ResponseErrorInterface;
10192
- use Predis\Cluster\PredisStrategy;
10193
- use Predis\Cluster\StrategyInterface;
10194
- use Predis\NotSupportedException;
10195
- use Predis\CommunicationException;
10196
- use Predis\Connection\Parameters;
10197
- use Predis\Replication\RoleException;
10198
- use Predis\Response\ServerException;
10199
- use Predis\Cluster\RedisStrategy as RedisClusterStrategy;
10200
-
10201
- /**
10202
- * Defines a group of Redis nodes in a master / slave replication setup.
10203
- *
10204
- * @author Daniele Alessandri <suppakilla@gmail.com>
10205
- */
10206
- interface ReplicationInterface extends AggregateConnectionInterface
10207
- {
10208
- /**
10209
- * Switches the internal connection instance in use.
10210
- *
10211
- * @param string $connection Alias of a connection
10212
- */
10213
- public function switchTo($connection);
10214
-
10215
- /**
10216
- * Returns the connection instance currently in use by the aggregate
10217
- * connection.
10218
- *
10219
- * @return NodeConnectionInterface
10220
- */
10221
- public function getCurrent();
10222
-
10223
- /**
10224
- * Returns the connection instance for the master Redis node.
10225
- *
10226
- * @return NodeConnectionInterface
10227
- */
10228
- public function getMaster();
10229
-
10230
- /**
10231
- * Returns a list of connection instances to slave nodes.
10232
- *
10233
- * @return NodeConnectionInterface
10234
- */
10235
- public function getSlaves();
10236
- }
10237
-
10238
- /**
10239
- * Defines a cluster of Redis servers formed by aggregating multiple connection
10240
- * instances to single Redis nodes.
10241
- *
10242
- * @author Daniele Alessandri <suppakilla@gmail.com>
10243
- */
10244
- interface ClusterInterface extends AggregateConnectionInterface
10245
- {
10246
- }
10247
-
10248
- /**
10249
- * Aggregate connection handling replication of Redis nodes configured in a
10250
- * single master / multiple slaves setup.
10251
- *
10252
- * @author Daniele Alessandri <suppakilla@gmail.com>
10253
- */
10254
- class MasterSlaveReplication implements ReplicationInterface
10255
- {
10256
- /**
10257
- * @var ReplicationStrategy
10258
- */
10259
- protected $strategy;
10260
-
10261
- /**
10262
- * @var NodeConnectionInterface
10263
- */
10264
- protected $master;
10265
-
10266
- /**
10267
- * @var NodeConnectionInterface[]
10268
- */
10269
- protected $slaves = array();
10270
-
10271
- /**
10272
- * @var NodeConnectionInterface
10273
- */
10274
- protected $current;
10275
-
10276
- /**
10277
- * @var bool
10278
- */
10279
- protected $autoDiscovery = false;
10280
-
10281
- /**
10282
- * @var FactoryInterface
10283
- */
10284
- protected $connectionFactory;
10285
-
10286
- /**
10287
- * {@inheritdoc}
10288
- */
10289
- public function __construct(ReplicationStrategy $strategy = null)
10290
- {
10291
- $this->strategy = $strategy ?: new ReplicationStrategy();
10292
- }
10293
-
10294
- /**
10295
- * Configures the automatic discovery of the replication configuration on failure.
10296
- *
10297
- * @param bool $value Enable or disable auto discovery.
10298
- */
10299
- public function setAutoDiscovery($value)
10300
- {
10301
- if (!$this->connectionFactory) {
10302
- throw new ClientException('Automatic discovery requires a connection factory');
10303
- }
10304
-
10305
- $this->autoDiscovery = (bool) $value;
10306
- }
10307
-
10308
- /**
10309
- * Sets the connection factory used to create the connections by the auto
10310
- * discovery procedure.
10311
- *
10312
- * @param FactoryInterface $connectionFactory Connection factory instance.
10313
- */
10314
- public function setConnectionFactory(FactoryInterface $connectionFactory)
10315
- {
10316
- $this->connectionFactory = $connectionFactory;
10317
- }
10318
-
10319
- /**
10320
- * Resets the connection state.
10321
- */
10322
- protected function reset()
10323
- {
10324
- $this->current = null;
10325
- }
10326
-
10327
- /**
10328
- * {@inheritdoc}
10329
- */
10330
- public function add(NodeConnectionInterface $connection)
10331
- {
10332
- $alias = $connection->getParameters()->alias;
10333
-
10334
- if ($alias === 'master') {
10335
- $this->master = $connection;
10336
- } else {
10337
- $this->slaves[$alias ?: "slave-$connection"] = $connection;
10338
- }
10339
-
10340
- $this->reset();
10341
- }
10342
-
10343
- /**
10344
- * {@inheritdoc}
10345
- */
10346
- public function remove(NodeConnectionInterface $connection)
10347
- {
10348
- if ($connection->getParameters()->alias === 'master') {
10349
- $this->master = null;
10350
- $this->reset();
10351
-
10352
- return true;
10353
- } else {
10354
- if (($id = array_search($connection, $this->slaves, true)) !== false) {
10355
- unset($this->slaves[$id]);
10356
- $this->reset();
10357
-
10358
- return true;
10359
- }
10360
- }
10361
-
10362
- return false;
10363
- }
10364
-
10365
- /**
10366
- * {@inheritdoc}
10367
- */
10368
- public function getConnection(CommandInterface $command)
10369
- {
10370
- if (!$this->current) {
10371
- if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) {
10372
- $this->current = $slave;
10373
- } else {
10374
- $this->current = $this->getMasterOrDie();
10375
- }
10376
-
10377
- return $this->current;
10378
- }
10379
-
10380
- if ($this->current === $master = $this->getMasterOrDie()) {
10381
- return $master;
10382
- }
10383
-
10384
- if (!$this->strategy->isReadOperation($command) || !$this->slaves) {
10385
- $this->current = $master;
10386
- }
10387
-
10388
- return $this->current;
10389
- }
10390
-
10391
- /**
10392
- * {@inheritdoc}
10393
- */
10394
- public function getConnectionById($connectionId)
10395
- {
10396
- if ($connectionId === 'master') {
10397
- return $this->master;
10398
- }
10399
-
10400
- if (isset($this->slaves[$connectionId])) {
10401
- return $this->slaves[$connectionId];
10402
- }
10403
-
10404
- return;
10405
- }
10406
-
10407
- /**
10408
- * {@inheritdoc}
10409
- */
10410
- public function switchTo($connection)
10411
- {
10412
- if (!$connection instanceof NodeConnectionInterface) {
10413
- $connection = $this->getConnectionById($connection);
10414
- }
10415
-
10416
- if (!$connection) {
10417
- throw new \InvalidArgumentException('Invalid connection or connection not found.');
10418
- }
10419
-
10420
- if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) {
10421
- throw new \InvalidArgumentException('Invalid connection or connection not found.');
10422
- }
10423
-
10424
- $this->current = $connection;
10425
- }
10426
-
10427
- /**
10428
- * Switches to the master server.
10429
- */
10430
- public function switchToMaster()
10431
- {
10432
- $this->switchTo('master');
10433
- }
10434
-
10435
- /**
10436
- * Switches to a random slave server.
10437
- */
10438
- public function switchToSlave()
10439
- {
10440
- $connection = $this->pickSlave();
10441
- $this->switchTo($connection);
10442
- }
10443
-
10444
- /**
10445
- * {@inheritdoc}
10446
- */
10447
- public function getCurrent()
10448
- {
10449
- return $this->current;
10450
- }
10451
-
10452
- /**
10453
- * {@inheritdoc}
10454
- */
10455
- public function getMaster()
10456
- {
10457
- return $this->master;
10458
- }
10459
-
10460
- /**
10461
- * Returns the connection associated to the master server.
10462
- *
10463
- * @return NodeConnectionInterface
10464
- */
10465
- private function getMasterOrDie()
10466
- {
10467
- if (!$connection = $this->getMaster()) {
10468
- throw new MissingMasterException('No master server available for replication');
10469
- }
10470
-
10471
- return $connection;
10472
- }
10473
-
10474
- /**
10475
- * {@inheritdoc}
10476
- */
10477
- public function getSlaves()
10478
- {
10479
- return array_values($this->slaves);
10480
- }
10481
-
10482
- /**
10483
- * Returns the underlying replication strategy.
10484
- *
10485
- * @return ReplicationStrategy
10486
- */
10487
- public function getReplicationStrategy()
10488
- {
10489
- return $this->strategy;
10490
- }
10491
-
10492
- /**
10493
- * Returns a random slave.
10494
- *
10495
- * @return NodeConnectionInterface
10496
- */
10497
- protected function pickSlave()
10498
- {
10499
- if ($this->slaves) {
10500
- return $this->slaves[array_rand($this->slaves)];
10501
- }
10502
- }
10503
-
10504
- /**
10505
- * {@inheritdoc}
10506
- */
10507
- public function isConnected()
10508
- {
10509
- return $this->current ? $this->current->isConnected() : false;
10510
- }
10511
-
10512
- /**
10513
- * {@inheritdoc}
10514
- */
10515
- public function connect()
10516
- {
10517
- if (!$this->current) {
10518
- if (!$this->current = $this->pickSlave()) {
10519
- if (!$this->current = $this->getMaster()) {
10520
- throw new ClientException('No available connection for replication');
10521
- }
10522
- }
10523
- }
10524
-
10525
- $this->current->connect();
10526
- }
10527
-
10528
- /**
10529
- * {@inheritdoc}
10530
- */
10531
- public function disconnect()
10532
- {
10533
- if ($this->master) {
10534
- $this->master->disconnect();
10535
- }
10536
-
10537
- foreach ($this->slaves as $connection) {
10538
- $connection->disconnect();
10539
- }
10540
- }
10541
-
10542
- /**
10543
- * Handles response from INFO.
10544
- *
10545
- * @param string $response
10546
- *
10547
- * @return array
10548
- */
10549
- private function handleInfoResponse($response)
10550
- {
10551
- $info = array();
10552
-
10553
- foreach (preg_split('/\r?\n/', $response) as $row) {
10554
- if (strpos($row, ':') === false) {
10555
- continue;
10556
- }
10557
-
10558
- list($k, $v) = explode(':', $row, 2);
10559
- $info[$k] = $v;
10560
- }
10561
-
10562
- return $info;
10563
- }
10564
-
10565
- /**
10566
- * Fetches the replication configuration from one of the servers.
10567
- */
10568
- public function discover()
10569
- {
10570
- if (!$this->connectionFactory) {
10571
- throw new ClientException('Discovery requires a connection factory');
10572
- }
10573
-
10574
- RETRY_FETCH: {
10575
- try {
10576
- if ($connection = $this->getMaster()) {
10577
- $this->discoverFromMaster($connection, $this->connectionFactory);
10578
- } elseif ($connection = $this->pickSlave()) {
10579
- $this->discoverFromSlave($connection, $this->connectionFactory);
10580
- } else {
10581
- throw new ClientException('No connection available for discovery');
10582
- }
10583
- } catch (ConnectionException $exception) {
10584
- $this->remove($connection);
10585
- goto RETRY_FETCH;
10586
- }
10587
- }
10588
- }
10589
-
10590
- /**
10591
- * Discovers the replication configuration by contacting the master node.
10592
- *
10593
- * @param NodeConnectionInterface $connection Connection to the master node.
10594
- * @param FactoryInterface $connectionFactory Connection factory instance.
10595
- */
10596
- protected function discoverFromMaster(NodeConnectionInterface $connection, FactoryInterface $connectionFactory)
10597
- {
10598
- $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION'));
10599
- $replication = $this->handleInfoResponse($response);
10600
-
10601
- if ($replication['role'] !== 'master') {
10602
- throw new ClientException("Role mismatch (expected master, got slave) [$connection]");
10603
- }
10604
-
10605
- $this->slaves = array();
10606
-
10607
- foreach ($replication as $k => $v) {
10608
- $parameters = null;
10609
-
10610
- if (strpos($k, 'slave') === 0 && preg_match('/ip=(?P<host>.*),port=(?P<port>\d+)/', $v, $parameters)) {
10611
- $slaveConnection = $connectionFactory->create(array(
10612
- 'host' => $parameters['host'],
10613
- 'port' => $parameters['port'],
10614
- ));
10615
-
10616
- $this->add($slaveConnection);
10617
- }
10618
- }
10619
- }
10620
-
10621
- /**
10622
- * Discovers the replication configuration by contacting one of the slaves.
10623
- *
10624
- * @param NodeConnectionInterface $connection Connection to one of the slaves.
10625
- * @param FactoryInterface $connectionFactory Connection factory instance.
10626
- */
10627
- protected function discoverFromSlave(NodeConnectionInterface $connection, FactoryInterface $connectionFactory)
10628
- {
10629
- $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION'));
10630
- $replication = $this->handleInfoResponse($response);
10631
-
10632
- if ($replication['role'] !== 'slave') {
10633
- throw new ClientException("Role mismatch (expected slave, got master) [$connection]");
10634
- }
10635
-
10636
- $masterConnection = $connectionFactory->create(array(
10637
- 'host' => $replication['master_host'],
10638
- 'port' => $replication['master_port'],
10639
- 'alias' => 'master',
10640
- ));
10641
-
10642
- $this->add($masterConnection);
10643
-
10644
- $this->discoverFromMaster($masterConnection, $connectionFactory);
10645
- }
10646
-
10647
- /**
10648
- * Retries the execution of a command upon slave failure.
10649
- *
10650
- * @param CommandInterface $command Command instance.
10651
- * @param string $method Actual method.
10652
- *
10653
- * @return mixed
10654
- */
10655
- private function retryCommandOnFailure(CommandInterface $command, $method)
10656
- {
10657
- RETRY_COMMAND: {
10658
- try {
10659
- $connection = $this->getConnection($command);
10660
- $response = $connection->$method($command);
10661
-
10662
- if ($response instanceof ResponseErrorInterface && $response->getErrorType() === 'LOADING') {
10663
- throw new ConnectionException($connection, "Redis is loading the dataset in memory [$connection]");
10664
- }
10665
- } catch (ConnectionException $exception) {
10666
- $connection = $exception->getConnection();
10667
- $connection->disconnect();
10668
-
10669
- if ($connection === $this->master && !$this->autoDiscovery) {
10670
- // Throw immediately when master connection is failing, even
10671
- // when the command represents a read-only operation, unless
10672
- // automatic discovery has been enabled.
10673
- throw $exception;
10674
- } else {
10675
- // Otherwise remove the failing slave and attempt to execute
10676
- // the command again on one of the remaining slaves...
10677
- $this->remove($connection);
10678
- }
10679
-
10680
- // ... that is, unless we have no more connections to use.
10681
- if (!$this->slaves && !$this->master) {
10682
- throw $exception;
10683
- } elseif ($this->autoDiscovery) {
10684
- $this->discover();
10685
- }
10686
-
10687
- goto RETRY_COMMAND;
10688
- } catch (MissingMasterException $exception) {
10689
- if ($this->autoDiscovery) {
10690
- $this->discover();
10691
- } else {
10692
- throw $exception;
10693
- }
10694
-
10695
- goto RETRY_COMMAND;
10696
- }
10697
- }
10698
-
10699
- return $response;
10700
- }
10701
-
10702
- /**
10703
- * {@inheritdoc}
10704
- */
10705
- public function writeRequest(CommandInterface $command)
10706
- {
10707
- $this->retryCommandOnFailure($command, __FUNCTION__);
10708
- }
10709
-
10710
- /**
10711
- * {@inheritdoc}
10712
- */
10713
- public function readResponse(CommandInterface $command)
10714
- {
10715
- return $this->retryCommandOnFailure($command, __FUNCTION__);
10716
- }
10717
-
10718
- /**
10719
- * {@inheritdoc}
10720
- */
10721
- public function executeCommand(CommandInterface $command)
10722
- {
10723
- return $this->retryCommandOnFailure($command, __FUNCTION__);
10724
- }
10725
-
10726
- /**
10727
- * {@inheritdoc}
10728
- */
10729
- public function __sleep()
10730
- {
10731
- return array('master', 'slaves', 'strategy');
10732
- }
10733
- }
10734
-
10735
- /**
10736
- * Abstraction for a cluster of aggregate connections to various Redis servers
10737
- * implementing client-side sharding based on pluggable distribution strategies.
10738
- *
10739
- * @author Daniele Alessandri <suppakilla@gmail.com>
10740
- *
10741
- * @todo Add the ability to remove connections from pool.
10742
- */
10743
- class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
10744
- {
10745
- private $pool;
10746
- private $strategy;
10747
- private $distributor;
10748
-
10749
- /**
10750
- * @param StrategyInterface $strategy Optional cluster strategy.
10751
- */
10752
- public function __construct(StrategyInterface $strategy = null)
10753
- {
10754
- $this->pool = array();
10755
- $this->strategy = $strategy ?: new PredisStrategy();
10756
- $this->distributor = $this->strategy->getDistributor();
10757
- }
10758
-
10759
- /**
10760
- * {@inheritdoc}
10761
- */
10762
- public function isConnected()
10763
- {
10764
- foreach ($this->pool as $connection) {
10765
- if ($connection->isConnected()) {
10766
- return true;
10767
- }
10768
- }
10769
-
10770
- return false;
10771
- }
10772
-
10773
- /**
10774
- * {@inheritdoc}
10775
- */
10776
- public function connect()
10777
- {
10778
- foreach ($this->pool as $connection) {
10779
- $connection->connect();
10780
- }
10781
- }
10782
-
10783
- /**
10784
- * {@inheritdoc}
10785
- */
10786
- public function disconnect()
10787
- {
10788
- foreach ($this->pool as $connection) {
10789
- $connection->disconnect();
10790
- }
10791
- }
10792
-
10793
- /**
10794
- * {@inheritdoc}
10795
- */
10796
- public function add(NodeConnectionInterface $connection)
10797
- {
10798
- $parameters = $connection->getParameters();
10799
-
10800
- if (isset($parameters->alias)) {
10801
- $this->pool[$parameters->alias] = $connection;
10802
- } else {
10803
- $this->pool[] = $connection;
10804
- }
10805
-
10806
- $weight = isset($parameters->weight) ? $parameters->weight : null;
10807
- $this->distributor->add($connection, $weight);
10808
- }
10809
-
10810
- /**
10811
- * {@inheritdoc}
10812
- */
10813
- public function remove(NodeConnectionInterface $connection)
10814
- {
10815
- if (($id = array_search($connection, $this->pool, true)) !== false) {
10816
- unset($this->pool[$id]);
10817
- $this->distributor->remove($connection);
10818
-
10819
- return true;
10820
- }
10821
-
10822
- return false;
10823
- }
10824
-
10825
- /**
10826
- * Removes a connection instance using its alias or index.
10827
- *
10828
- * @param string $connectionID Alias or index of a connection.
10829
- *
10830
- * @return bool Returns true if the connection was in the pool.
10831
- */
10832
- public function removeById($connectionID)
10833
- {
10834
- if ($connection = $this->getConnectionById($connectionID)) {
10835
- return $this->remove($connection);
10836
- }
10837
-
10838
- return false;
10839
- }
10840
-
10841
- /**
10842
- * {@inheritdoc}
10843
- */
10844
- public function getConnection(CommandInterface $command)
10845
- {
10846
- $slot = $this->strategy->getSlot($command);
10847
-
10848
- if (!isset($slot)) {
10849
- throw new NotSupportedException(
10850
- "Cannot use '{$command->getId()}' over clusters of connections."
10851
- );
10852
- }
10853
-
10854
- $node = $this->distributor->getBySlot($slot);
10855
-
10856
- return $node;
10857
- }
10858
-
10859
- /**
10860
- * {@inheritdoc}
10861
- */
10862
- public function getConnectionById($connectionID)
10863
- {
10864
- return isset($this->pool[$connectionID]) ? $this->pool[$connectionID] : null;
10865
- }
10866
-
10867
- /**
10868
- * Retrieves a connection instance from the cluster using a key.
10869
- *
10870
- * @param string $key Key string.
10871
- *
10872
- * @return NodeConnectionInterface
10873
- */
10874
- public function getConnectionByKey($key)
10875
- {
10876
- $hash = $this->strategy->getSlotByKey($key);
10877
- $node = $this->distributor->getBySlot($hash);
10878
-
10879
- return $node;
10880
- }
10881
-
10882
- /**
10883
- * Returns the underlying command hash strategy used to hash commands by
10884
- * using keys found in their arguments.
10885
- *
10886
- * @return StrategyInterface
10887
- */
10888
- public function getClusterStrategy()
10889
- {
10890
- return $this->strategy;
10891
- }
10892
-
10893
- /**
10894
- * {@inheritdoc}
10895
- */
10896
- public function count()
10897
- {
10898
- return count($this->pool);
10899
- }
10900
-
10901
- /**
10902
- * {@inheritdoc}
10903
- */
10904
- public function getIterator()
10905
- {
10906
- return new \ArrayIterator($this->pool);
10907
- }
10908
-
10909
- /**
10910
- * {@inheritdoc}
10911
- */
10912
- public function writeRequest(CommandInterface $command)
10913
- {
10914
- $this->getConnection($command)->writeRequest($command);
10915
- }
10916
-
10917
- /**
10918
- * {@inheritdoc}
10919
- */
10920
- public function readResponse(CommandInterface $command)
10921
- {
10922
- return $this->getConnection($command)->readResponse($command);
10923
- }
10924
-
10925
- /**
10926
- * {@inheritdoc}
10927
- */
10928
- public function executeCommand(CommandInterface $command)
10929
- {
10930
- return $this->getConnection($command)->executeCommand($command);
10931
- }
10932
-
10933
- /**
10934
- * Executes the specified Redis command on all the nodes of a cluster.
10935
- *
10936
- * @param CommandInterface $command A Redis command.
10937
- *
10938
- * @return array
10939
- */
10940
- public function executeCommandOnNodes(CommandInterface $command)
10941
- {
10942
- $responses = array();
10943
-
10944
- foreach ($this->pool as $connection) {
10945
- $responses[] = $connection->executeCommand($command);
10946
- }
10947
-
10948
- return $responses;
10949
- }
10950
- }
10951
-
10952
- /**
10953
- * @author Daniele Alessandri <suppakilla@gmail.com>
10954
- * @author Ville Mattila <ville@eventio.fi>
10955
- */
10956
- class SentinelReplication implements ReplicationInterface
10957
- {
10958
- /**
10959
- * @var NodeConnectionInterface
10960
- */
10961
- protected $master;
10962
-
10963
- /**
10964
- * @var NodeConnectionInterface[]
10965
- */
10966
- protected $slaves = array();
10967
-
10968
- /**
10969
- * @var NodeConnectionInterface
10970
- */
10971
- protected $current;
10972
-
10973
- /**
10974
- * @var string
10975
- */
10976
- protected $service;
10977
-
10978
- /**
10979
- * @var ConnectionFactoryInterface
10980
- */
10981
- protected $connectionFactory;
10982
-
10983
- /**
10984
- * @var ReplicationStrategy
10985
- */
10986
- protected $strategy;
10987
-
10988
- /**
10989
- * @var NodeConnectionInterface[]
10990
- */
10991
- protected $sentinels = array();
10992
-
10993
- /**
10994
- * @var NodeConnectionInterface
10995
- */
10996
- protected $sentinelConnection;
10997
-
10998
- /**
10999
- * @var float
11000
- */
11001
- protected $sentinelTimeout = 0.100;
11002
-
11003
- /**
11004
- * Max number of automatic retries of commands upon server failure.
11005
- *
11006
- * -1 = unlimited retry attempts
11007
- * 0 = no retry attempts (fails immediatly)
11008
- * n = fail only after n retry attempts
11009
- *
11010
- * @var int
11011
- */
11012
- protected $retryLimit = 20;
11013
-
11014
- /**
11015
- * Time to wait in milliseconds before fetching a new configuration from one
11016
- * of the sentinel servers.
11017
- *
11018
- * @var int
11019
- */
11020
- protected $retryWait = 1000;
11021
-
11022
- /**
11023
- * Flag for automatic fetching of available sentinels.
11024
- *
11025
- * @var bool
11026
- */
11027
- protected $updateSentinels = false;
11028
-
11029
- /**
11030
- * @param string $service Name of the service for autodiscovery.
11031
- * @param array $sentinels Sentinel servers connection parameters.
11032
- * @param ConnectionFactoryInterface $connectionFactory Connection factory instance.
11033
- * @param ReplicationStrategy $strategy Replication strategy instance.
11034
- */
11035
- public function __construct(
11036
- $service,
11037
- array $sentinels,
11038
- ConnectionFactoryInterface $connectionFactory,
11039
- ReplicationStrategy $strategy = null
11040
- ) {
11041
- $this->sentinels = $sentinels;
11042
- $this->service = $service;
11043
- $this->connectionFactory = $connectionFactory;
11044
- $this->strategy = $strategy ?: new ReplicationStrategy();
11045
- }
11046
-
11047
- /**
11048
- * Sets a default timeout for connections to sentinels.
11049
- *
11050
- * When "timeout" is present in the connection parameters of sentinels, its
11051
- * value overrides the default sentinel timeout.
11052
- *
11053
- * @param float $timeout Timeout value.
11054
- */
11055
- public function setSentinelTimeout($timeout)
11056
- {
11057
- $this->sentinelTimeout = (float) $timeout;
11058
- }
11059
-
11060
- /**
11061
- * Sets the maximum number of retries for commands upon server failure.
11062
- *
11063
- * -1 = unlimited retry attempts
11064
- * 0 = no retry attempts (fails immediatly)
11065
- * n = fail only after n retry attempts
11066
- *
11067
- * @param int $retry Number of retry attempts.
11068
- */
11069
- public function setRetryLimit($retry)
11070
- {
11071
- $this->retryLimit = (int) $retry;
11072
- }
11073
-
11074
- /**
11075
- * Sets the time to wait (in seconds) before fetching a new configuration
11076
- * from one of the sentinels.
11077
- *
11078
- * @param float $seconds Time to wait before the next attempt.
11079
- */
11080
- public function setRetryWait($seconds)
11081
- {
11082
- $this->retryWait = (float) $seconds;
11083
- }
11084
-
11085
- /**
11086
- * Set automatic fetching of available sentinels.
11087
- *
11088
- * @param bool $update Enable or disable automatic updates.
11089
- */
11090
- public function setUpdateSentinels($update)
11091
- {
11092
- $this->updateSentinels = (bool) $update;
11093
- }
11094
-
11095
- /**
11096
- * Resets the current connection.
11097
- */
11098
- protected function reset()
11099
- {
11100
- $this->current = null;
11101
- }
11102
-
11103
- /**
11104
- * Wipes the current list of master and slaves nodes.
11105
- */
11106
- protected function wipeServerList()
11107
- {
11108
- $this->reset();
11109
-
11110
- $this->master = null;
11111
- $this->slaves = array();
11112
- }
11113
-
11114
- /**
11115
- * {@inheritdoc}
11116
- */
11117
- public function add(NodeConnectionInterface $connection)
11118
- {
11119
- $alias = $connection->getParameters()->alias;
11120
-
11121
- if ($alias === 'master') {
11122
- $this->master = $connection;
11123
- } else {
11124
- $this->slaves[$alias ?: count($this->slaves)] = $connection;
11125
- }
11126
-
11127
- $this->reset();
11128
- }
11129
-
11130
- /**
11131
- * {@inheritdoc}
11132
- */
11133
- public function remove(NodeConnectionInterface $connection)
11134
- {
11135
- if ($connection === $this->master) {
11136
- $this->master = null;
11137
- $this->reset();
11138
-
11139
- return true;
11140
- }
11141
-
11142
- if (false !== $id = array_search($connection, $this->slaves, true)) {
11143
- unset($this->slaves[$id]);
11144
- $this->reset();
11145
-
11146
- return true;
11147
- }
11148
-
11149
- return false;
11150
- }
11151
-
11152
- /**
11153
- * Creates a new connection to a sentinel server.
11154
- *
11155
- * @return NodeConnectionInterface
11156
- */
11157
- protected function createSentinelConnection($parameters)
11158
- {
11159
- if ($parameters instanceof NodeConnectionInterface) {
11160
- return $parameters;
11161
- }
11162
-
11163
- if (is_string($parameters)) {
11164
- $parameters = Parameters::parse($parameters);
11165
- }
11166
-
11167
- if (is_array($parameters)) {
11168
- // We explicitly set "database" and "password" to null,
11169
- // so that no AUTH and SELECT command is send to the sentinels.
11170
- $parameters['database'] = null;
11171
- $parameters['password'] = null;
11172
-
11173
- if (!isset($parameters['timeout'])) {
11174
- $parameters['timeout'] = $this->sentinelTimeout;
11175
- }
11176
- }
11177
-
11178
- $connection = $this->connectionFactory->create($parameters);
11179
-
11180
- return $connection;
11181
- }
11182
-
11183
- /**
11184
- * Returns the current sentinel connection.
11185
- *
11186
- * If there is no active sentinel connection, a new connection is created.
11187
- *
11188
- * @return NodeConnectionInterface
11189
- */
11190
- public function getSentinelConnection()
11191
- {
11192
- if (!$this->sentinelConnection) {
11193
- if (!$this->sentinels) {
11194
- throw new \Predis\ClientException('No sentinel server available for autodiscovery.');
11195
- }
11196
-
11197
- $sentinel = array_shift($this->sentinels);
11198
- $this->sentinelConnection = $this->createSentinelConnection($sentinel);
11199
- }
11200
-
11201
- return $this->sentinelConnection;
11202
- }
11203
-
11204
- /**
11205
- * Fetches an updated list of sentinels from a sentinel.
11206
- */
11207
- public function updateSentinels()
11208
- {
11209
- SENTINEL_QUERY: {
11210
- $sentinel = $this->getSentinelConnection();
11211
-
11212
- try {
11213
- $payload = $sentinel->executeCommand(
11214
- RawCommand::create('SENTINEL', 'sentinels', $this->service)
11215
- );
11216
-
11217
- $this->sentinels = array();
11218
- // NOTE: sentinel server does not return itself, so we add it back.
11219
- $this->sentinels[] = $sentinel->getParameters()->toArray();
11220
-
11221
- foreach ($payload as $sentinel) {
11222
- $this->sentinels[] = array(
11223
- 'host' => $sentinel[3],
11224
- 'port' => $sentinel[5],
11225
- );
11226
- }
11227
- } catch (ConnectionException $exception) {
11228
- $this->sentinelConnection = null;
11229
-
11230
- goto SENTINEL_QUERY;
11231
- }
11232
- }
11233
- }
11234
-
11235
- /**
11236
- * Fetches the details for the master and slave servers from a sentinel.
11237
- */
11238
- public function querySentinel()
11239
- {
11240
- $this->wipeServerList();
11241
-
11242
- $this->updateSentinels();
11243
- $this->getMaster();
11244
- $this->getSlaves();
11245
- }
11246
-
11247
- /**
11248
- * Handles error responses returned by redis-sentinel.
11249
- *
11250
- * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
11251
- * @param ErrorResponseInterface $error Error response.
11252
- */
11253
- private function handleSentinelErrorResponse(NodeConnectionInterface $sentinel, ErrorResponseInterface $error)
11254
- {
11255
- if ($error->getErrorType() === 'IDONTKNOW') {
11256
- throw new ConnectionException($sentinel, $error->getMessage());
11257
- } else {
11258
- throw new ServerException($error->getMessage());
11259
- }
11260
- }
11261
-
11262
- /**
11263
- * Fetches the details for the master server from a sentinel.
11264
- *
11265
- * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
11266
- * @param string $service Name of the service.
11267
- *
11268
- * @return array
11269
- */
11270
- protected function querySentinelForMaster(NodeConnectionInterface $sentinel, $service)
11271
- {
11272
- $payload = $sentinel->executeCommand(
11273
- RawCommand::create('SENTINEL', 'get-master-addr-by-name', $service)
11274
- );
11275
-
11276
- if ($payload === null) {
11277
- throw new ServerException('ERR No such master with that name');
11278
- }
11279
-
11280
- if ($payload instanceof ErrorResponseInterface) {
11281
- $this->handleSentinelErrorResponse($sentinel, $payload);
11282
- }
11283
-
11284
- return array(
11285
- 'host' => $payload[0],
11286
- 'port' => $payload[1],
11287
- 'alias' => 'master',
11288
- );
11289
- }
11290
-
11291
- /**
11292
- * Fetches the details for the slave servers from a sentinel.
11293
- *
11294
- * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
11295
- * @param string $service Name of the service.
11296
- *
11297
- * @return array
11298
- */
11299
- protected function querySentinelForSlaves(NodeConnectionInterface $sentinel, $service)
11300
- {
11301
- $slaves = array();
11302
-
11303
- $payload = $sentinel->executeCommand(
11304
- RawCommand::create('SENTINEL', 'slaves', $service)
11305
- );
11306
-
11307
- if ($payload instanceof ErrorResponseInterface) {
11308
- $this->handleSentinelErrorResponse($sentinel, $payload);
11309
- }
11310
-
11311
- foreach ($payload as $slave) {
11312
- $flags = explode(',', $slave[9]);
11313
-
11314
- if (array_intersect($flags, array('s_down', 'o_down', 'disconnected'))) {
11315
- continue;
11316
- }
11317
-
11318
- $slaves[] = array(
11319
- 'host' => $slave[3],
11320
- 'port' => $slave[5],
11321
- 'alias' => "slave-$slave[1]",
11322
- );
11323
- }
11324
-
11325
- return $slaves;
11326
- }
11327
-
11328
- /**
11329
- * {@inheritdoc}
11330
- */
11331
- public function getCurrent()
11332
- {
11333
- return $this->current;
11334
- }
11335
-
11336
- /**
11337
- * {@inheritdoc}
11338
- */
11339
- public function getMaster()
11340
- {
11341
- if ($this->master) {
11342
- return $this->master;
11343
- }
11344
-
11345
- if ($this->updateSentinels) {
11346
- $this->updateSentinels();
11347
- }
11348
-
11349
- SENTINEL_QUERY: {
11350
- $sentinel = $this->getSentinelConnection();
11351
-
11352
- try {
11353
- $masterParameters = $this->querySentinelForMaster($sentinel, $this->service);
11354
- $masterConnection = $this->connectionFactory->create($masterParameters);
11355
-
11356
- $this->add($masterConnection);
11357
- } catch (ConnectionException $exception) {
11358
- $this->sentinelConnection = null;
11359
-
11360
- goto SENTINEL_QUERY;
11361
- }
11362
- }
11363
-
11364
- return $masterConnection;
11365
- }
11366
-
11367
- /**
11368
- * {@inheritdoc}
11369
- */
11370
- public function getSlaves()
11371
- {
11372
- if ($this->slaves) {
11373
- return array_values($this->slaves);
11374
- }
11375
-
11376
- if ($this->updateSentinels) {
11377
- $this->updateSentinels();
11378
- }
11379
-
11380
- SENTINEL_QUERY: {
11381
- $sentinel = $this->getSentinelConnection();
11382
-
11383
- try {
11384
- $slavesParameters = $this->querySentinelForSlaves($sentinel, $this->service);
11385
-
11386
- foreach ($slavesParameters as $slaveParameters) {
11387
- $this->add($this->connectionFactory->create($slaveParameters));
11388
- }
11389
- } catch (ConnectionException $exception) {
11390
- $this->sentinelConnection = null;
11391
-
11392
- goto SENTINEL_QUERY;
11393
- }
11394
- }
11395
-
11396
- return array_values($this->slaves ?: array());
11397
- }
11398
-
11399
- /**
11400
- * Returns a random slave.
11401
- *
11402
- * @return NodeConnectionInterface
11403
- */
11404
- protected function pickSlave()
11405
- {
11406
- if ($slaves = $this->getSlaves()) {
11407
- return $slaves[rand(1, count($slaves)) - 1];
11408
- }
11409
- }
11410
-
11411
- /**
11412
- * Returns the connection instance in charge for the given command.
11413
- *
11414
- * @param CommandInterface $command Command instance.
11415
- *
11416
- * @return NodeConnectionInterface
11417
- */
11418
- private function getConnectionInternal(CommandInterface $command)
11419
- {
11420
- if (!$this->current) {
11421
- if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) {
11422
- $this->current = $slave;
11423
- } else {
11424
- $this->current = $this->getMaster();
11425
- }
11426
-
11427
- return $this->current;
11428
- }
11429
-
11430
- if ($this->current === $this->master) {
11431
- return $this->current;
11432
- }
11433
-
11434
- if (!$this->strategy->isReadOperation($command)) {
11435
- $this->current = $this->getMaster();
11436
- }
11437
-
11438
- return $this->current;
11439
- }
11440
-
11441
- /**
11442
- * Asserts that the specified connection matches an expected role.
11443
- *
11444
- * @param NodeConnectionInterface $connection Connection to a redis server.
11445
- * @param string $role Expected role of the server ("master", "slave" or "sentinel").
11446
- *
11447
- * @throws RoleException
11448
- */
11449
- protected function assertConnectionRole(NodeConnectionInterface $connection, $role)
11450
- {
11451
- $role = strtolower($role);
11452
- $actualRole = $connection->executeCommand(RawCommand::create('ROLE'));
11453
-
11454
- if ($role !== $actualRole[0]) {
11455
- throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]");
11456
- }
11457
- }
11458
-
11459
- /**
11460
- * {@inheritdoc}
11461
- */
11462
- public function getConnection(CommandInterface $command)
11463
- {
11464
- $connection = $this->getConnectionInternal($command);
11465
-
11466
- if (!$connection->isConnected()) {
11467
- // When we do not have any available slave in the pool we can expect
11468
- // read-only operations to hit the master server.
11469
- $expectedRole = $this->strategy->isReadOperation($command) && $this->slaves ? 'slave' : 'master';
11470
- $this->assertConnectionRole($connection, $expectedRole);
11471
- }
11472
-
11473
- return $connection;
11474
- }
11475
-
11476
- /**
11477
- * {@inheritdoc}
11478
- */
11479
- public function getConnectionById($connectionId)
11480
- {
11481
- if ($connectionId === 'master') {
11482
- return $this->getMaster();
11483
- }
11484
-
11485
- $this->getSlaves();
11486
-
11487
- if (isset($this->slaves[$connectionId])) {
11488
- return $this->slaves[$connectionId];
11489
- }
11490
- }
11491
-
11492
- /**
11493
- * {@inheritdoc}
11494
- */
11495
- public function switchTo($connection)
11496
- {
11497
- if (!$connection instanceof NodeConnectionInterface) {
11498
- $connection = $this->getConnectionById($connection);
11499
- }
11500
-
11501
- if ($connection && $connection === $this->current) {
11502
- return;
11503
- }
11504
-
11505
- if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) {
11506
- throw new \InvalidArgumentException('Invalid connection or connection not found.');
11507
- }
11508
-
11509
- $connection->connect();
11510
-
11511
- if ($this->current) {
11512
- $this->current->disconnect();
11513
- }
11514
-
11515
- $this->current = $connection;
11516
- }
11517
-
11518
- /**
11519
- * Switches to the master server.
11520
- */
11521
- public function switchToMaster()
11522
- {
11523
- $this->switchTo('master');
11524
- }
11525
-
11526
- /**
11527
- * Switches to a random slave server.
11528
- */
11529
- public function switchToSlave()
11530
- {
11531
- $connection = $this->pickSlave();
11532
- $this->switchTo($connection);
11533
- }
11534
-
11535
- /**
11536
- * {@inheritdoc}
11537
- */
11538
- public function isConnected()
11539
- {
11540
- return $this->current ? $this->current->isConnected() : false;
11541
- }
11542
-
11543
- /**
11544
- * {@inheritdoc}
11545
- */
11546
- public function connect()
11547
- {
11548
- if (!$this->current) {
11549
- if (!$this->current = $this->pickSlave()) {
11550
- $this->current = $this->getMaster();
11551
- }
11552
- }
11553
-
11554
- $this->current->connect();
11555
- }
11556
-
11557
- /**
11558
- * {@inheritdoc}
11559
- */
11560
- public function disconnect()
11561
- {
11562
- if ($this->master) {
11563
- $this->master->disconnect();
11564
- }
11565
-
11566
- foreach ($this->slaves as $connection) {
11567
- $connection->disconnect();
11568
- }
11569
- }
11570
-
11571
- /**
11572
- * Retries the execution of a command upon server failure after asking a new
11573
- * configuration to one of the sentinels.
11574
- *
11575
- * @param CommandInterface $command Command instance.
11576
- * @param string $method Actual method.
11577
- *
11578
- * @return mixed
11579
- */
11580
- private function retryCommandOnFailure(CommandInterface $command, $method)
11581
- {
11582
- $retries = 0;
11583
-
11584
- SENTINEL_RETRY: {
11585
- try {
11586
- $response = $this->getConnection($command)->$method($command);
11587
- } catch (CommunicationException $exception) {
11588
- $this->wipeServerList();
11589
- $exception->getConnection()->disconnect();
11590
-
11591
- if ($retries == $this->retryLimit) {
11592
- throw $exception;
11593
- }
11594
-
11595
- usleep($this->retryWait * 1000);
11596
-
11597
- ++$retries;
11598
- goto SENTINEL_RETRY;
11599
- }
11600
- }
11601
-
11602
- return $response;
11603
- }
11604
-
11605
- /**
11606
- * {@inheritdoc}
11607
- */
11608
- public function writeRequest(CommandInterface $command)
11609
- {
11610
- $this->retryCommandOnFailure($command, __FUNCTION__);
11611
- }
11612
-
11613
- /**
11614
- * {@inheritdoc}
11615
- */
11616
- public function readResponse(CommandInterface $command)
11617
- {
11618
- return $this->retryCommandOnFailure($command, __FUNCTION__);
11619
- }
11620
-
11621
- /**
11622
- * {@inheritdoc}
11623
- */
11624
- public function executeCommand(CommandInterface $command)
11625
- {
11626
- return $this->retryCommandOnFailure($command, __FUNCTION__);
11627
- }
11628
-
11629
- /**
11630
- * Returns the underlying replication strategy.
11631
- *
11632
- * @return ReplicationStrategy
11633
- */
11634
- public function getReplicationStrategy()
11635
- {
11636
- return $this->strategy;
11637
- }
11638
-
11639
- /**
11640
- * {@inheritdoc}
11641
- */
11642
- public function __sleep()
11643
- {
11644
- return array(
11645
- 'master', 'slaves', 'service', 'sentinels', 'connectionFactory', 'strategy',
11646
- );
11647
- }
11648
- }
11649
-
11650
- /**
11651
- * Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0).
11652
- *
11653
- * This connection backend offers smart support for redis-cluster by handling
11654
- * automatic slots map (re)generation upon -MOVED or -ASK responses returned by
11655
- * Redis when redirecting a client to a different node.
11656
- *
11657
- * The cluster can be pre-initialized using only a subset of the actual nodes in
11658
- * the cluster, Predis will do the rest by adjusting the slots map and creating
11659
- * the missing underlying connection instances on the fly.
11660
- *
11661
- * It is possible to pre-associate connections to a slots range with the "slots"
11662
- * parameter in the form "$first-$last". This can greatly reduce runtime node
11663
- * guessing and redirections.
11664
- *
11665
- * It is also possible to ask for the full and updated slots map directly to one
11666
- * of the nodes and optionally enable such a behaviour upon -MOVED redirections.
11667
- * Asking for the cluster configuration to Redis is actually done by issuing a
11668
- * CLUSTER SLOTS command to a random node in the pool.
11669
- *
11670
- * @author Daniele Alessandri <suppakilla@gmail.com>
11671
- */
11672
- class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
11673
- {
11674
- private $useClusterSlots = true;
11675
- private $pool = array();
11676
- private $slots = array();
11677
- private $slotsMap;
11678
- private $strategy;
11679
- private $connections;
11680
- private $retryLimit = 5;
11681
-
11682
- /**
11683
- * @param FactoryInterface $connections Optional connection factory.
11684
- * @param StrategyInterface $strategy Optional cluster strategy.
11685
- */
11686
- public function __construct(
11687
- FactoryInterface $connections,
11688
- StrategyInterface $strategy = null
11689
- ) {
11690
- $this->connections = $connections;
11691
- $this->strategy = $strategy ?: new RedisClusterStrategy();
11692
- }
11693
-
11694
- /**
11695
- * Sets the maximum number of retries for commands upon server failure.
11696
- *
11697
- * -1 = unlimited retry attempts
11698
- * 0 = no retry attempts (fails immediatly)
11699
- * n = fail only after n retry attempts
11700
- *
11701
- * @param int $retry Number of retry attempts.
11702
- */
11703
- public function setRetryLimit($retry)
11704
- {
11705
- $this->retryLimit = (int) $retry;
11706
- }
11707
-
11708
- /**
11709
- * {@inheritdoc}
11710
- */
11711
- public function isConnected()
11712
- {
11713
- foreach ($this->pool as $connection) {
11714
- if ($connection->isConnected()) {
11715
- return true;
11716
- }
11717
- }
11718
-
11719
- return false;
11720
- }
11721
-
11722
- /**
11723
- * {@inheritdoc}
11724
- */
11725
- public function connect()
11726
- {
11727
- if ($connection = $this->getRandomConnection()) {
11728
- $connection->connect();
11729
- }
11730
- }
11731
-
11732
- /**
11733
- * {@inheritdoc}
11734
- */
11735
- public function disconnect()
11736
- {
11737
- foreach ($this->pool as $connection) {
11738
- $connection->disconnect();
11739
- }
11740
- }
11741
-
11742
- /**
11743
- * {@inheritdoc}
11744
- */
11745
- public function add(NodeConnectionInterface $connection)
11746
- {
11747
- $this->pool[(string) $connection] = $connection;
11748
- unset($this->slotsMap);
11749
- }
11750
-
11751
- /**
11752
- * {@inheritdoc}
11753
- */
11754
- public function remove(NodeConnectionInterface $connection)
11755
- {
11756
- if (false !== $id = array_search($connection, $this->pool, true)) {
11757
- unset(
11758
- $this->pool[$id],
11759
- $this->slotsMap
11760
- );
11761
-
11762
- $this->slots = array_diff($this->slots, array($connection));
11763
-
11764
- return true;
11765
- }
11766
-
11767
- return false;
11768
- }
11769
-
11770
- /**
11771
- * Removes a connection instance by using its identifier.
11772
- *
11773
- * @param string $connectionID Connection identifier.
11774
- *
11775
- * @return bool True if the connection was in the pool.
11776
- */
11777
- public function removeById($connectionID)
11778
- {
11779
- if (isset($this->pool[$connectionID])) {
11780
- unset(
11781
- $this->pool[$connectionID],
11782
- $this->slotsMap
11783
- );
11784
-
11785
- return true;
11786
- }
11787
-
11788
- return false;
11789
- }
11790
-
11791
- /**
11792
- * Generates the current slots map by guessing the cluster configuration out
11793
- * of the connection parameters of the connections in the pool.
11794
- *
11795
- * Generation is based on the same algorithm used by Redis to generate the
11796
- * cluster, so it is most effective when all of the connections supplied on
11797
- * initialization have the "slots" parameter properly set accordingly to the
11798
- * current cluster configuration.
11799
- *
11800
- * @return array
11801
- */
11802
- public function buildSlotsMap()
11803
- {
11804
- $this->slotsMap = array();
11805
-
11806
- foreach ($this->pool as $connectionID => $connection) {
11807
- $parameters = $connection->getParameters();
11808
-
11809
- if (!isset($parameters->slots)) {
11810
- continue;
11811
- }
11812
-
11813
- foreach (explode(',', $parameters->slots) as $slotRange) {
11814
- $slots = explode('-', $slotRange, 2);
11815
-
11816
- if (!isset($slots[1])) {
11817
- $slots[1] = $slots[0];
11818
- }
11819
-
11820
- $this->setSlots($slots[0], $slots[1], $connectionID);
11821
- }
11822
- }
11823
-
11824
- return $this->slotsMap;
11825
- }
11826
-
11827
- /**
11828
- * Queries the specified node of the cluster to fetch the updated slots map.
11829
- *
11830
- * When the connection fails, this method tries to execute the same command
11831
- * on a different connection picked at random from the pool of known nodes,
11832
- * up until the retry limit is reached.
11833
- *
11834
- * @param NodeConnectionInterface $connection Connection to a node of the cluster.
11835
- *
11836
- * @return mixed
11837
- */
11838
- private function queryClusterNodeForSlotsMap(NodeConnectionInterface $connection)
11839
- {
11840
- $retries = 0;
11841
- $command = RawCommand::create('CLUSTER', 'SLOTS');
11842
-
11843
- RETRY_COMMAND: {
11844
- try {
11845
- $response = $connection->executeCommand($command);
11846
- } catch (ConnectionException $exception) {
11847
- $connection = $exception->getConnection();
11848
- $connection->disconnect();
11849
-
11850
- $this->remove($connection);
11851
-
11852
- if ($retries === $this->retryLimit) {
11853
- throw $exception;
11854
- }
11855
-
11856
- if (!$connection = $this->getRandomConnection()) {
11857
- throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`');
11858
- }
11859
-
11860
- ++$retries;
11861
- goto RETRY_COMMAND;
11862
- }
11863
- }
11864
-
11865
- return $response;
11866
- }
11867
-
11868
- /**
11869
- * Generates an updated slots map fetching the cluster configuration using
11870
- * the CLUSTER SLOTS command against the specified node or a random one from
11871
- * the pool.
11872
- *
11873
- * @param NodeConnectionInterface $connection Optional connection instance.
11874
- *
11875
- * @return array
11876
- */
11877
- public function askSlotsMap(NodeConnectionInterface $connection = null)
11878
- {
11879
- if (!$connection && !$connection = $this->getRandomConnection()) {
11880
- return array();
11881
- }
11882
-
11883
- $this->resetSlotsMap();
11884
-
11885
- $response = $this->queryClusterNodeForSlotsMap($connection);
11886
-
11887
- foreach ($response as $slots) {
11888
- // We only support master servers for now, so we ignore subsequent
11889
- // elements in the $slots array identifying slaves.
11890
- list($start, $end, $master) = $slots;
11891
-
11892
- if ($master[0] === '') {
11893
- $this->setSlots($start, $end, (string) $connection);
11894
- } else {
11895
- $this->setSlots($start, $end, "{$master[0]}:{$master[1]}");
11896
- }
11897
- }
11898
-
11899
- return $this->slotsMap;
11900
- }
11901
-
11902
- /**
11903
- * Resets the slots map cache.
11904
- */
11905
- public function resetSlotsMap()
11906
- {
11907
- $this->slotsMap = array();
11908
- }
11909
-
11910
- /**
11911
- * Returns the current slots map for the cluster.
11912
- *
11913
- * The order of the returned $slot => $server dictionary is not guaranteed.
11914
- *
11915
- * @return array
11916
- */
11917
- public function getSlotsMap()
11918
- {
11919
- if (!isset($this->slotsMap)) {
11920
- $this->slotsMap = array();
11921
- }
11922
-
11923
- return $this->slotsMap;
11924
- }
11925
-
11926
- /**
11927
- * Pre-associates a connection to a slots range to avoid runtime guessing.
11928
- *
11929
- * @param int $first Initial slot of the range.
11930
- * @param int $last Last slot of the range.
11931
- * @param NodeConnectionInterface|string $connection ID or connection instance.
11932
- *
11933
- * @throws \OutOfBoundsException
11934
- */
11935
- public function setSlots($first, $last, $connection)
11936
- {
11937
- if ($first < 0x0000 || $first > 0x3FFF ||
11938
- $last < 0x0000 || $last > 0x3FFF ||
11939
- $last < $first
11940
- ) {
11941
- throw new \OutOfBoundsException(
11942
- "Invalid slot range for $connection: [$first-$last]."
11943
- );
11944
- }
11945
-
11946
- $slots = array_fill($first, $last - $first + 1, (string) $connection);
11947
- $this->slotsMap = $this->getSlotsMap() + $slots;
11948
- }
11949
-
11950
- /**
11951
- * Guesses the correct node associated to a given slot using a precalculated
11952
- * slots map, falling back to the same logic used by Redis to initialize a
11953
- * cluster (best-effort).
11954
- *
11955
- * @param int $slot Slot index.
11956
- *
11957
- * @return string Connection ID.
11958
- */
11959
- protected function guessNode($slot)
11960
- {
11961
- if (!$this->pool) {
11962
- throw new ClientException('No connections available in the pool');
11963
- }
11964
-
11965
- if (!isset($this->slotsMap)) {
11966
- $this->buildSlotsMap();
11967
- }
11968
-
11969
- if (isset($this->slotsMap[$slot])) {
11970
- return $this->slotsMap[$slot];
11971
- }
11972
-
11973
- $count = count($this->pool);
11974
- $index = min((int) ($slot / (int) (16384 / $count)), $count - 1);
11975
- $nodes = array_keys($this->pool);
11976
-
11977
- return $nodes[$index];
11978
- }
11979
-
11980
- /**
11981
- * Creates a new connection instance from the given connection ID.
11982
- *
11983
- * @param string $connectionID Identifier for the connection.
11984
- *
11985
- * @return NodeConnectionInterface
11986
- */
11987
- protected function createConnection($connectionID)
11988
- {
11989
- $separator = strrpos($connectionID, ':');
11990
-
11991
- return $this->connections->create(array(
11992
- 'host' => substr($connectionID, 0, $separator),
11993
- 'port' => substr($connectionID, $separator + 1),
11994
- ));
11995
- }
11996
-
11997
- /**
11998
- * {@inheritdoc}
11999
- */
12000
- public function getConnection(CommandInterface $command)
12001
- {
12002
- $slot = $this->strategy->getSlot($command);
12003
-
12004
- if (!isset($slot)) {
12005
- throw new NotSupportedException(
12006
- "Cannot use '{$command->getId()}' with redis-cluster."
12007
- );
12008
- }
12009
-
12010
- if (isset($this->slots[$slot])) {
12011
- return $this->slots[$slot];
12012
- } else {
12013
- return $this->getConnectionBySlot($slot);
12014
- }
12015
- }
12016
-
12017
- /**
12018
- * Returns the connection currently associated to a given slot.
12019
- *
12020
- * @param int $slot Slot index.
12021
- *
12022
- * @throws \OutOfBoundsException
12023
- *
12024
- * @return NodeConnectionInterface
12025
- */
12026
- public function getConnectionBySlot($slot)
12027
- {
12028
- if ($slot < 0x0000 || $slot > 0x3FFF) {
12029
- throw new \OutOfBoundsException("Invalid slot [$slot].");
12030
- }
12031
-
12032
- if (isset($this->slots[$slot])) {
12033
- return $this->slots[$slot];
12034
- }
12035
-
12036
- $connectionID = $this->guessNode($slot);
12037
-
12038
- if (!$connection = $this->getConnectionById($connectionID)) {
12039
- $connection = $this->createConnection($connectionID);
12040
- $this->pool[$connectionID] = $connection;
12041
- }
12042
-
12043
- return $this->slots[$slot] = $connection;
12044
- }
12045
-
12046
- /**
12047
- * {@inheritdoc}
12048
- */
12049
- public function getConnectionById($connectionID)
12050
- {
12051
- if (isset($this->pool[$connectionID])) {
12052
- return $this->pool[$connectionID];
12053
- }
12054
- }
12055
-
12056
- /**
12057
- * Returns a random connection from the pool.
12058
- *
12059
- * @return NodeConnectionInterface|null
12060
- */
12061
- protected function getRandomConnection()
12062
- {
12063
- if ($this->pool) {
12064
- return $this->pool[array_rand($this->pool)];
12065
- }
12066
- }
12067
-
12068
- /**
12069
- * Permanently associates the connection instance to a new slot.
12070
- * The connection is added to the connections pool if not yet included.
12071
- *
12072
- * @param NodeConnectionInterface $connection Connection instance.
12073
- * @param int $slot Target slot index.
12074
- */
12075
- protected function move(NodeConnectionInterface $connection, $slot)
12076
- {
12077
- $this->pool[(string) $connection] = $connection;
12078
- $this->slots[(int) $slot] = $connection;
12079
- }
12080
-
12081
- /**
12082
- * Handles -ERR responses returned by Redis.
12083
- *
12084
- * @param CommandInterface $command Command that generated the -ERR response.
12085
- * @param ErrorResponseInterface $error Redis error response object.
12086
- *
12087
- * @return mixed
12088
- */
12089
- protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $error)
12090
- {
12091
- $details = explode(' ', $error->getMessage(), 2);
12092
-
12093
- switch ($details[0]) {
12094
- case 'MOVED':
12095
- return $this->onMovedResponse($command, $details[1]);
12096
-
12097
- case 'ASK':
12098
- return $this->onAskResponse($command, $details[1]);
12099
-
12100
- default:
12101
- return $error;
12102
- }
12103
- }
12104
-
12105
- /**
12106
- * Handles -MOVED responses by executing again the command against the node
12107
- * indicated by the Redis response.
12108
- *
12109
- * @param CommandInterface $command Command that generated the -MOVED response.
12110
- * @param string $details Parameters of the -MOVED response.
12111
- *
12112
- * @return mixed
12113
- */
12114
- protected function onMovedResponse(CommandInterface $command, $details)
12115
- {
12116
- list($slot, $connectionID) = explode(' ', $details, 2);
12117
-
12118
- if (!$connection = $this->getConnectionById($connectionID)) {
12119
- $connection = $this->createConnection($connectionID);
12120
- }
12121
-
12122
- if ($this->useClusterSlots) {
12123
- $this->askSlotsMap($connection);
12124
- }
12125
-
12126
- $this->move($connection, $slot);
12127
- $response = $this->executeCommand($command);
12128
-
12129
- return $response;
12130
- }
12131
-
12132
- /**
12133
- * Handles -ASK responses by executing again the command against the node
12134
- * indicated by the Redis response.
12135
- *
12136
- * @param CommandInterface $command Command that generated the -ASK response.
12137
- * @param string $details Parameters of the -ASK response.
12138
- *
12139
- * @return mixed
12140
- */
12141
- protected function onAskResponse(CommandInterface $command, $details)
12142
- {
12143
- list($slot, $connectionID) = explode(' ', $details, 2);
12144
-
12145
- if (!$connection = $this->getConnectionById($connectionID)) {
12146
- $connection = $this->createConnection($connectionID);
12147
- }
12148
-
12149
- $connection->executeCommand(RawCommand::create('ASKING'));
12150
- $response = $connection->executeCommand($command);
12151
-
12152
- return $response;
12153
- }
12154
-
12155
- /**
12156
- * Ensures that a command is executed one more time on connection failure.
12157
- *
12158
- * The connection to the node that generated the error is evicted from the
12159
- * pool before trying to fetch an updated slots map from another node. If
12160
- * the new slots map points to an unreachable server the client gives up and
12161
- * throws the exception as the nodes participating in the cluster may still
12162
- * have to agree that something changed in the configuration of the cluster.
12163
- *
12164
- * @param CommandInterface $command Command instance.
12165
- * @param string $method Actual method.
12166
- *
12167
- * @return mixed
12168
- */
12169
- private function retryCommandOnFailure(CommandInterface $command, $method)
12170
- {
12171
- $failure = false;
12172
-
12173
- RETRY_COMMAND: {
12174
- try {
12175
- $response = $this->getConnection($command)->$method($command);
12176
- } catch (ConnectionException $exception) {
12177
- $connection = $exception->getConnection();
12178
- $connection->disconnect();
12179
-
12180
- $this->remove($connection);
12181
-
12182
- if ($failure) {
12183
- throw $exception;
12184
- } elseif ($this->useClusterSlots) {
12185
- $this->askSlotsMap();
12186
- }
12187
-
12188
- $failure = true;
12189
-
12190
- goto RETRY_COMMAND;
12191
- }
12192
- }
12193
-
12194
- return $response;
12195
- }
12196
-
12197
- /**
12198
- * {@inheritdoc}
12199
- */
12200
- public function writeRequest(CommandInterface $command)
12201
- {
12202
- $this->retryCommandOnFailure($command, __FUNCTION__);
12203
- }
12204
-
12205
- /**
12206
- * {@inheritdoc}
12207
- */
12208
- public function readResponse(CommandInterface $command)
12209
- {
12210
- return $this->retryCommandOnFailure($command, __FUNCTION__);
12211
- }
12212
-
12213
- /**
12214
- * {@inheritdoc}
12215
- */
12216
- public function executeCommand(CommandInterface $command)
12217
- {
12218
- $response = $this->retryCommandOnFailure($command, __FUNCTION__);
12219
-
12220
- if ($response instanceof ErrorResponseInterface) {
12221
- return $this->onErrorResponse($command, $response);
12222
- }
12223
-
12224
- return $response;
12225
- }
12226
-
12227
- /**
12228
- * {@inheritdoc}
12229
- */
12230
- public function count()
12231
- {
12232
- return count($this->pool);
12233
- }
12234
-
12235
- /**
12236
- * {@inheritdoc}
12237
- */
12238
- public function getIterator()
12239
- {
12240
- if ($this->useClusterSlots) {
12241
- $slotsmap = $this->getSlotsMap() ?: $this->askSlotsMap();
12242
- } else {
12243
- $slotsmap = $this->getSlotsMap() ?: $this->buildSlotsMap();
12244
- }
12245
-
12246
- $connections = array();
12247
-
12248
- foreach (array_unique($slotsmap) as $node) {
12249
- if (!$connection = $this->getConnectionById($node)) {
12250
- $this->add($connection = $this->createConnection($node));
12251
- }
12252
-
12253
- $connections[] = $connection;
12254
- }
12255
-
12256
- return new \ArrayIterator($connections);
12257
- }
12258
-
12259
- /**
12260
- * Returns the underlying command hash strategy used to hash commands by
12261
- * using keys found in their arguments.
12262
- *
12263
- * @return StrategyInterface
12264
- */
12265
- public function getClusterStrategy()
12266
- {
12267
- return $this->strategy;
12268
- }
12269
-
12270
- /**
12271
- * Returns the underlying connection factory used to create new connection
12272
- * instances to Redis nodes indicated by redis-cluster.
12273
- *
12274
- * @return FactoryInterface
12275
- */
12276
- public function getConnectionFactory()
12277
- {
12278
- return $this->connections;
12279
- }
12280
-
12281
- /**
12282
- * Enables automatic fetching of the current slots map from one of the nodes
12283
- * using the CLUSTER SLOTS command. This option is enabled by default as
12284
- * asking the current slots map to Redis upon -MOVED responses may reduce
12285
- * overhead by eliminating the trial-and-error nature of the node guessing
12286
- * procedure, mostly when targeting many keys that would end up in a lot of
12287
- * redirections.
12288
- *
12289
- * The slots map can still be manually fetched using the askSlotsMap()
12290
- * method whether or not this option is enabled.
12291
- *
12292
- * @param bool $value Enable or disable the use of CLUSTER SLOTS.
12293
- */
12294
- public function useClusterSlots($value)
12295
- {
12296
- $this->useClusterSlots = (bool) $value;
12297
- }
12298
- }
12299
-
12300
- /* --------------------------------------------------------------------------- */
12301
-
12302
- namespace Predis\Collection\Iterator;
12303
-
12304
- use Predis\ClientInterface;
12305
- use Predis\NotSupportedException;
12306
-
12307
- /**
12308
- * Provides the base implementation for a fully-rewindable PHP iterator that can
12309
- * incrementally iterate over cursor-based collections stored on Redis using the
12310
- * commands in the `SCAN` family.
12311
- *
12312
- * Given their incremental nature with multiple fetches, these kind of iterators
12313
- * offer limited guarantees about the returned elements because the collection
12314
- * can change several times during the iteration process.
12315
- *
12316
- * @see http://redis.io/commands/scan
12317
- *
12318
- * @author Daniele Alessandri <suppakilla@gmail.com>
12319
- */
12320
- abstract class CursorBasedIterator implements \Iterator
12321
- {
12322
- protected $client;
12323
- protected $match;
12324
- protected $count;
12325
-
12326
- protected $valid;
12327
- protected $fetchmore;
12328
- protected $elements;
12329
- protected $cursor;
12330
- protected $position;
12331
- protected $current;
12332
-
12333
- /**
12334
- * @param ClientInterface $client Client connected to Redis.
12335
- * @param string $match Pattern to match during the server-side iteration.
12336
- * @param int $count Hint used by Redis to compute the number of results per iteration.
12337
- */
12338
- public function __construct(ClientInterface $client, $match = null, $count = null)
12339
- {
12340
- $this->client = $client;
12341
- $this->match = $match;
12342
- $this->count = $count;
12343
-
12344
- $this->reset();
12345
- }
12346
-
12347
- /**
12348
- * Ensures that the client supports the specified Redis command required to
12349
- * fetch elements from the server to perform the iteration.
12350
- *
12351
- * @param ClientInterface $client Client connected to Redis.
12352
- * @param string $commandID Command ID.
12353
- *
12354
- * @throws NotSupportedException
12355
- */
12356
- protected function requiredCommand(ClientInterface $client, $commandID)
12357
- {
12358
- if (!$client->getProfile()->supportsCommand($commandID)) {
12359
- throw new NotSupportedException("The current profile does not support '$commandID'.");
12360
- }
12361
- }
12362
-
12363
- /**
12364
- * Resets the inner state of the iterator.
12365
- */
12366
- protected function reset()
12367
- {
12368
- $this->valid = true;
12369
- $this->fetchmore = true;
12370
- $this->elements = array();
12371
- $this->cursor = 0;
12372
- $this->position = -1;
12373
- $this->current = null;
12374
- }
12375
-
12376
- /**
12377
- * Returns an array of options for the `SCAN` command.
12378
- *
12379
- * @return array
12380
- */
12381
- protected function getScanOptions()
12382
- {
12383
- $options = array();
12384
-
12385
- if (strlen($this->match) > 0) {
12386
- $options['MATCH'] = $this->match;
12387
- }
12388
-
12389
- if ($this->count > 0) {
12390
- $options['COUNT'] = $this->count;
12391
- }
12392
-
12393
- return $options;
12394
- }
12395
-
12396
- /**
12397
- * Fetches a new set of elements from the remote collection, effectively
12398
- * advancing the iteration process.
12399
- *
12400
- * @return array
12401
- */
12402
- abstract protected function executeCommand();
12403
-
12404
- /**
12405
- * Populates the local buffer of elements fetched from the server during
12406
- * the iteration.
12407
- */
12408
- protected function fetch()
12409
- {
12410
- list($cursor, $elements) = $this->executeCommand();
12411
-
12412
- if (!$cursor) {
12413
- $this->fetchmore = false;
12414
- }
12415
-
12416
- $this->cursor = $cursor;
12417
- $this->elements = $elements;
12418
- }
12419
-
12420
- /**
12421
- * Extracts next values for key() and current().
12422
- */
12423
- protected function extractNext()
12424
- {
12425
- ++$this->position;
12426
- $this->current = array_shift($this->elements);
12427
- }
12428
-
12429
- /**
12430
- * {@inheritdoc}
12431
- */
12432
- public function rewind()
12433
- {
12434
- $this->reset();
12435
- $this->next();
12436
- }
12437
-
12438
- /**
12439
- * {@inheritdoc}
12440
- */
12441
- public function current()
12442
- {
12443
- return $this->current;
12444
- }
12445
-
12446
- /**
12447
- * {@inheritdoc}
12448
- */
12449
- public function key()
12450
- {
12451
- return $this->position;
12452
- }
12453
-
12454
- /**
12455
- * {@inheritdoc}
12456
- */
12457
- public function next()
12458
- {
12459
- tryFetch: {
12460
- if (!$this->elements && $this->fetchmore) {
12461
- $this->fetch();
12462
- }
12463
-
12464
- if ($this->elements) {
12465
- $this->extractNext();
12466
- } elseif ($this->cursor) {
12467
- goto tryFetch;
12468
- } else {
12469
- $this->valid = false;
12470
- }
12471
- }
12472
- }
12473
-
12474
- /**
12475
- * {@inheritdoc}
12476
- */
12477
- public function valid()
12478
- {
12479
- return $this->valid;
12480
- }
12481
- }
12482
-
12483
- /**
12484
- * Abstracts the iteration of members stored in a sorted set by leveraging the
12485
- * ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
12486
- *
12487
- * @author Daniele Alessandri <suppakilla@gmail.com>
12488
- *
12489
- * @link http://redis.io/commands/scan
12490
- */
12491
- class SortedSetKey extends CursorBasedIterator
12492
- {
12493
- protected $key;
12494
-
12495
- /**
12496
- * {@inheritdoc}
12497
- */
12498
- public function __construct(ClientInterface $client, $key, $match = null, $count = null)
12499
- {
12500
- $this->requiredCommand($client, 'ZSCAN');
12501
-
12502
- parent::__construct($client, $match, $count);
12503
-
12504
- $this->key = $key;
12505
- }
12506
-
12507
- /**
12508
- * {@inheritdoc}
12509
- */
12510
- protected function executeCommand()
12511
- {
12512
- return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions());
12513
- }
12514
-
12515
- /**
12516
- * {@inheritdoc}
12517
- */
12518
- protected function extractNext()
12519
- {
12520
- $this->position = key($this->elements);
12521
- $this->current = current($this->elements);
12522
-
12523
- unset($this->elements[$this->position]);
12524
- }
12525
- }
12526
-
12527
- /**
12528
- * Abstracts the iteration of members stored in a set by leveraging the SSCAN
12529
- * command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
12530
- *
12531
- * @author Daniele Alessandri <suppakilla@gmail.com>
12532
- *
12533
- * @link http://redis.io/commands/scan
12534
- */
12535
- class SetKey extends CursorBasedIterator
12536
- {
12537
- protected $key;
12538
-
12539
- /**
12540
- * {@inheritdoc}
12541
- */
12542
- public function __construct(ClientInterface $client, $key, $match = null, $count = null)
12543
- {
12544
- $this->requiredCommand($client, 'SSCAN');
12545
-
12546
- parent::__construct($client, $match, $count);
12547
-
12548
- $this->key = $key;
12549
- }
12550
-
12551
- /**
12552
- * {@inheritdoc}
12553
- */
12554
- protected function executeCommand()
12555
- {
12556
- return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions());
12557
- }
12558
- }
12559
-
12560
- /**
12561
- * Abstracts the iteration of the keyspace on a Redis instance by leveraging the
12562
- * SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
12563
- *
12564
- * @author Daniele Alessandri <suppakilla@gmail.com>
12565
- *
12566
- * @link http://redis.io/commands/scan
12567
- */
12568
- class Keyspace extends CursorBasedIterator
12569
- {
12570
- /**
12571
- * {@inheritdoc}
12572
- */
12573
- public function __construct(ClientInterface $client, $match = null, $count = null)
12574
- {
12575
- $this->requiredCommand($client, 'SCAN');
12576
-
12577
- parent::__construct($client, $match, $count);
12578
- }
12579
-
12580
- /**
12581
- * {@inheritdoc}
12582
- */
12583
- protected function executeCommand()
12584
- {
12585
- return $this->client->scan($this->cursor, $this->getScanOptions());
12586
- }
12587
- }
12588
-
12589
- /**
12590
- * Abstracts the iteration of fields and values of an hash by leveraging the
12591
- * HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
12592
- *
12593
- * @author Daniele Alessandri <suppakilla@gmail.com>
12594
- *
12595
- * @link http://redis.io/commands/scan
12596
- */
12597
- class HashKey extends CursorBasedIterator
12598
- {
12599
- protected $key;
12600
-
12601
- /**
12602
- * {@inheritdoc}
12603
- */
12604
- public function __construct(ClientInterface $client, $key, $match = null, $count = null)
12605
- {
12606
- $this->requiredCommand($client, 'HSCAN');
12607
-
12608
- parent::__construct($client, $match, $count);
12609
-
12610
- $this->key = $key;
12611
- }
12612
-
12613
- /**
12614
- * {@inheritdoc}
12615
- */
12616
- protected function executeCommand()
12617
- {
12618
- return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions());
12619
- }
12620
-
12621
- /**
12622
- * {@inheritdoc}
12623
- */
12624
- protected function extractNext()
12625
- {
12626
- $this->position = key($this->elements);
12627
- $this->current = current($this->elements);
12628
-
12629
- unset($this->elements[$this->position]);
12630
- }
12631
- }
12632
-
12633
- /**
12634
- * Abstracts the iteration of items stored in a list by leveraging the LRANGE
12635
- * command wrapped in a fully-rewindable PHP iterator.
12636
- *
12637
- * This iterator tries to emulate the behaviour of cursor-based iterators based
12638
- * on the SCAN-family of commands introduced in Redis <= 2.8, meaning that due
12639
- * to its incremental nature with multiple fetches it can only offer limited
12640
- * guarantees on the returned elements because the collection can change several
12641
- * times (trimmed, deleted, overwritten) during the iteration process.
12642
- *
12643
- * @author Daniele Alessandri <suppakilla@gmail.com>
12644
- *
12645
- * @link http://redis.io/commands/lrange
12646
- */
12647
- class ListKey implements \Iterator
12648
- {
12649
- protected $client;
12650
- protected $count;
12651
- protected $key;
12652
-
12653
- protected $valid;
12654
- protected $fetchmore;
12655
- protected $elements;
12656
- protected $position;
12657
- protected $current;
12658
-
12659
- /**
12660
- * @param ClientInterface $client Client connected to Redis.
12661
- * @param string $key Redis list key.
12662
- * @param int $count Number of items retrieved on each fetch operation.
12663
- *
12664
- * @throws \InvalidArgumentException
12665
- */
12666
- public function __construct(ClientInterface $client, $key, $count = 10)
12667
- {
12668
- $this->requiredCommand($client, 'LRANGE');
12669
-
12670
- if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) {
12671
- throw new \InvalidArgumentException('The $count argument must be a positive integer.');
12672
- }
12673
-
12674
- $this->client = $client;
12675
- $this->key = $key;
12676
- $this->count = $count;
12677
-
12678
- $this->reset();
12679
- }
12680
-
12681
- /**
12682
- * Ensures that the client instance supports the specified Redis command
12683
- * required to fetch elements from the server to perform the iteration.
12684
- *
12685
- * @param ClientInterface $client Client connected to Redis.
12686
- * @param string $commandID Command ID.
12687
- *
12688
- * @throws NotSupportedException
12689
- */
12690
- protected function requiredCommand(ClientInterface $client, $commandID)
12691
- {
12692
- if (!$client->getProfile()->supportsCommand($commandID)) {
12693
- throw new NotSupportedException("The current profile does not support '$commandID'.");
12694
- }
12695
- }
12696
-
12697
- /**
12698
- * Resets the inner state of the iterator.
12699
- */
12700
- protected function reset()
12701
- {
12702
- $this->valid = true;
12703
- $this->fetchmore = true;
12704
- $this->elements = array();
12705
- $this->position = -1;
12706
- $this->current = null;
12707
- }
12708
-
12709
- /**
12710
- * Fetches a new set of elements from the remote collection, effectively
12711
- * advancing the iteration process.
12712
- *
12713
- * @return array
12714
- */
12715
- protected function executeCommand()
12716
- {
12717
- return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count);
12718
- }
12719
-
12720
- /**
12721
- * Populates the local buffer of elements fetched from the server during the
12722
- * iteration.
12723
- */
12724
- protected function fetch()
12725
- {
12726
- $elements = $this->executeCommand();
12727
-
12728
- if (count($elements) < $this->count) {
12729
- $this->fetchmore = false;
12730
- }
12731
-
12732
- $this->elements = $elements;
12733
- }
12734
-
12735
- /**
12736
- * Extracts next values for key() and current().
12737
- */
12738
- protected function extractNext()
12739
- {
12740
- ++$this->position;
12741
- $this->current = array_shift($this->elements);
12742
- }
12743
-
12744
- /**
12745
- * {@inheritdoc}
12746
- */
12747
- public function rewind()
12748
- {
12749
- $this->reset();
12750
- $this->next();
12751
- }
12752
-
12753
- /**
12754
- * {@inheritdoc}
12755
- */
12756
- public function current()
12757
- {
12758
- return $this->current;
12759
- }
12760
-
12761
- /**
12762
- * {@inheritdoc}
12763
- */
12764
- public function key()
12765
- {
12766
- return $this->position;
12767
- }
12768
-
12769
- /**
12770
- * {@inheritdoc}
12771
- */
12772
- public function next()
12773
- {
12774
- if (!$this->elements && $this->fetchmore) {
12775
- $this->fetch();
12776
- }
12777
-
12778
- if ($this->elements) {
12779
- $this->extractNext();
12780
- } else {
12781
- $this->valid = false;
12782
- }
12783
- }
12784
-
12785
- /**
12786
- * {@inheritdoc}
12787
- */
12788
- public function valid()
12789
- {
12790
- return $this->valid;
12791
- }
12792
- }
12793
-
12794
- /* --------------------------------------------------------------------------- */
12795
-
12796
- namespace Predis\Cluster;
12797
-
12798
- use Predis\Command\CommandInterface;
12799
- use Predis\Command\ScriptCommand;
12800
- use Predis\Cluster\Hash\CRC16;
12801
- use Predis\Cluster\Hash\HashGeneratorInterface;
12802
- use Predis\NotSupportedException;
12803
- use Predis\Cluster\Distributor\DistributorInterface;
12804
- use Predis\Cluster\Distributor\HashRing;
12805
-
12806
- /**
12807
- * Interface for classes defining the strategy used to calculate an hash out of
12808
- * keys extracted from supported commands.
12809
- *
12810
- * This is mostly useful to support clustering via client-side sharding.
12811
- *
12812
- * @author Daniele Alessandri <suppakilla@gmail.com>
12813
- */
12814
- interface StrategyInterface
12815
- {
12816
- /**
12817
- * Returns a slot for the given command used for clustering distribution or
12818
- * NULL when this is not possible.
12819
- *
12820
- * @param CommandInterface $command Command instance.
12821
- *
12822
- * @return int
12823
- */
12824
- public function getSlot(CommandInterface $command);
12825
-
12826
- /**
12827
- * Returns a slot for the given key used for clustering distribution or NULL
12828
- * when this is not possible.
12829
- *
12830
- * @param string $key Key string.
12831
- *
12832
- * @return int
12833
- */
12834
- public function getSlotByKey($key);
12835
-
12836
- /**
12837
- * Returns a distributor instance to be used by the cluster.
12838
- *
12839
- * @return DistributorInterface
12840
- */
12841
- public function getDistributor();
12842
- }
12843
-
12844
- /**
12845
- * Common class implementing the logic needed to support clustering strategies.
12846
- *
12847
- * @author Daniele Alessandri <suppakilla@gmail.com>
12848
- */
12849
- abstract class ClusterStrategy implements StrategyInterface
12850
- {
12851
- protected $commands;
12852
-
12853
- /**
12854
- *
12855
- */
12856
- public function __construct()
12857
- {
12858
- $this->commands = $this->getDefaultCommands();
12859
- }
12860
-
12861
- /**
12862
- * Returns the default map of supported commands with their handlers.
12863
- *
12864
- * @return array
12865
- */
12866
- protected function getDefaultCommands()
12867
- {
12868
- $getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
12869
- $getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
12870
-
12871
- return array(
12872
- /* commands operating on the key space */
12873
- 'EXISTS' => $getKeyFromAllArguments,
12874
- 'DEL' => $getKeyFromAllArguments,
12875
- 'TYPE' => $getKeyFromFirstArgument,
12876
- 'EXPIRE' => $getKeyFromFirstArgument,
12877
- 'EXPIREAT' => $getKeyFromFirstArgument,
12878
- 'PERSIST' => $getKeyFromFirstArgument,
12879
- 'PEXPIRE' => $getKeyFromFirstArgument,
12880
- 'PEXPIREAT' => $getKeyFromFirstArgument,
12881
- 'TTL' => $getKeyFromFirstArgument,
12882
- 'PTTL' => $getKeyFromFirstArgument,
12883
- 'SORT' => array($this, 'getKeyFromSortCommand'),
12884
- 'DUMP' => $getKeyFromFirstArgument,
12885
- 'RESTORE' => $getKeyFromFirstArgument,
12886
-
12887
- /* commands operating on string values */
12888
- 'APPEND' => $getKeyFromFirstArgument,
12889
- 'DECR' => $getKeyFromFirstArgument,
12890
- 'DECRBY' => $getKeyFromFirstArgument,
12891
- 'GET' => $getKeyFromFirstArgument,
12892
- 'GETBIT' => $getKeyFromFirstArgument,
12893
- 'MGET' => $getKeyFromAllArguments,
12894
- 'SET' => $getKeyFromFirstArgument,
12895
- 'GETRANGE' => $getKeyFromFirstArgument,
12896
- 'GETSET' => $getKeyFromFirstArgument,
12897
- 'INCR' => $getKeyFromFirstArgument,
12898
- 'INCRBY' => $getKeyFromFirstArgument,
12899
- 'INCRBYFLOAT' => $getKeyFromFirstArgument,
12900
- 'SETBIT' => $getKeyFromFirstArgument,
12901
- 'SETEX' => $getKeyFromFirstArgument,
12902
- 'MSET' => array($this, 'getKeyFromInterleavedArguments'),
12903
- 'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
12904
- 'SETNX' => $getKeyFromFirstArgument,
12905
- 'SETRANGE' => $getKeyFromFirstArgument,
12906
- 'STRLEN' => $getKeyFromFirstArgument,
12907
- 'SUBSTR' => $getKeyFromFirstArgument,
12908
- 'BITOP' => array($this, 'getKeyFromBitOp'),
12909
- 'BITCOUNT' => $getKeyFromFirstArgument,
12910
- 'BITFIELD' => $getKeyFromFirstArgument,
12911
-
12912
- /* commands operating on lists */
12913
- 'LINSERT' => $getKeyFromFirstArgument,
12914
- 'LINDEX' => $getKeyFromFirstArgument,
12915
- 'LLEN' => $getKeyFromFirstArgument,
12916
- 'LPOP' => $getKeyFromFirstArgument,
12917
- 'RPOP' => $getKeyFromFirstArgument,
12918
- 'RPOPLPUSH' => $getKeyFromAllArguments,
12919
- 'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
12920
- 'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
12921
- 'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
12922
- 'LPUSH' => $getKeyFromFirstArgument,
12923
- 'LPUSHX' => $getKeyFromFirstArgument,
12924
- 'RPUSH' => $getKeyFromFirstArgument,
12925
- 'RPUSHX' => $getKeyFromFirstArgument,
12926
- 'LRANGE' => $getKeyFromFirstArgument,
12927
- 'LREM' => $getKeyFromFirstArgument,
12928
- 'LSET' => $getKeyFromFirstArgument,
12929
- 'LTRIM' => $getKeyFromFirstArgument,
12930
-
12931
- /* commands operating on sets */
12932
- 'SADD' => $getKeyFromFirstArgument,
12933
- 'SCARD' => $getKeyFromFirstArgument,
12934
- 'SDIFF' => $getKeyFromAllArguments,
12935
- 'SDIFFSTORE' => $getKeyFromAllArguments,
12936
- 'SINTER' => $getKeyFromAllArguments,
12937
- 'SINTERSTORE' => $getKeyFromAllArguments,
12938
- 'SUNION' => $getKeyFromAllArguments,
12939
- 'SUNIONSTORE' => $getKeyFromAllArguments,
12940
- 'SISMEMBER' => $getKeyFromFirstArgument,
12941
- 'SMEMBERS' => $getKeyFromFirstArgument,
12942
- 'SSCAN' => $getKeyFromFirstArgument,
12943
- 'SPOP' => $getKeyFromFirstArgument,
12944
- 'SRANDMEMBER' => $getKeyFromFirstArgument,
12945
- 'SREM' => $getKeyFromFirstArgument,
12946
-
12947
- /* commands operating on sorted sets */
12948
- 'ZADD' => $getKeyFromFirstArgument,
12949
- 'ZCARD' => $getKeyFromFirstArgument,
12950
- 'ZCOUNT' => $getKeyFromFirstArgument,
12951
- 'ZINCRBY' => $getKeyFromFirstArgument,
12952
- 'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
12953
- 'ZRANGE' => $getKeyFromFirstArgument,
12954
- 'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
12955
- 'ZRANK' => $getKeyFromFirstArgument,
12956
- 'ZREM' => $getKeyFromFirstArgument,
12957
- 'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
12958
- 'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
12959
- 'ZREVRANGE' => $getKeyFromFirstArgument,
12960
- 'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
12961
- 'ZREVRANK' => $getKeyFromFirstArgument,
12962
- 'ZSCORE' => $getKeyFromFirstArgument,
12963
- 'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
12964
- 'ZSCAN' => $getKeyFromFirstArgument,
12965
- 'ZLEXCOUNT' => $getKeyFromFirstArgument,
12966
- 'ZRANGEBYLEX' => $getKeyFromFirstArgument,
12967
- 'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
12968
- 'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
12969
-
12970
- /* commands operating on hashes */
12971
- 'HDEL' => $getKeyFromFirstArgument,
12972
- 'HEXISTS' => $getKeyFromFirstArgument,
12973
- 'HGET' => $getKeyFromFirstArgument,
12974
- 'HGETALL' => $getKeyFromFirstArgument,
12975
- 'HMGET' => $getKeyFromFirstArgument,
12976
- 'HMSET' => $getKeyFromFirstArgument,
12977
- 'HINCRBY' => $getKeyFromFirstArgument,
12978
- 'HINCRBYFLOAT' => $getKeyFromFirstArgument,
12979
- 'HKEYS' => $getKeyFromFirstArgument,
12980
- 'HLEN' => $getKeyFromFirstArgument,
12981
- 'HSET' => $getKeyFromFirstArgument,
12982
- 'HSETNX' => $getKeyFromFirstArgument,
12983
- 'HVALS' => $getKeyFromFirstArgument,
12984
- 'HSCAN' => $getKeyFromFirstArgument,
12985
- 'HSTRLEN' => $getKeyFromFirstArgument,
12986
-
12987
- /* commands operating on HyperLogLog */
12988
- 'PFADD' => $getKeyFromFirstArgument,
12989
- 'PFCOUNT' => $getKeyFromAllArguments,
12990
- 'PFMERGE' => $getKeyFromAllArguments,
12991
-
12992
- /* scripting */
12993
- 'EVAL' => array($this, 'getKeyFromScriptingCommands'),
12994
- 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
12995
-
12996
- /* commands performing geospatial operations */
12997
- 'GEOADD' => $getKeyFromFirstArgument,
12998
- 'GEOHASH' => $getKeyFromFirstArgument,
12999
- 'GEOPOS' => $getKeyFromFirstArgument,
13000
- 'GEODIST' => $getKeyFromFirstArgument,
13001
- 'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'),
13002
- 'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'),
13003
- );
13004
- }
13005
-
13006
- /**
13007
- * Returns the list of IDs for the supported commands.
13008
- *
13009
- * @return array
13010
- */
13011
- public function getSupportedCommands()
13012
- {
13013
- return array_keys($this->commands);
13014
- }
13015
-
13016
- /**
13017
- * Sets an handler for the specified command ID.
13018
- *
13019
- * The signature of the callback must have a single parameter of type
13020
- * Predis\Command\CommandInterface.
13021
- *
13022
- * When the callback argument is omitted or NULL, the previously associated
13023
- * handler for the specified command ID is removed.
13024
- *
13025
- * @param string $commandID Command ID.
13026
- * @param mixed $callback A valid callable object, or NULL to unset the handler.
13027
- *
13028
- * @throws \InvalidArgumentException
13029
- */
13030
- public function setCommandHandler($commandID, $callback = null)
13031
- {
13032
- $commandID = strtoupper($commandID);
13033
-
13034
- if (!isset($callback)) {
13035
- unset($this->commands[$commandID]);
13036
-
13037
- return;
13038
- }
13039
-
13040
- if (!is_callable($callback)) {
13041
- throw new \InvalidArgumentException(
13042
- 'The argument must be a callable object or NULL.'
13043
- );
13044
- }
13045
-
13046
- $this->commands[$commandID] = $callback;
13047
- }
13048
-
13049
- /**
13050
- * Extracts the key from the first argument of a command instance.
13051
- *
13052
- * @param CommandInterface $command Command instance.
13053
- *
13054
- * @return string
13055
- */
13056
- protected function getKeyFromFirstArgument(CommandInterface $command)
13057
- {
13058
- return $command->getArgument(0);
13059
- }
13060
-
13061
- /**
13062
- * Extracts the key from a command with multiple keys only when all keys in
13063
- * the arguments array produce the same hash.
13064
- *
13065
- * @param CommandInterface $command Command instance.
13066
- *
13067
- * @return string|null
13068
- */
13069
- protected function getKeyFromAllArguments(CommandInterface $command)
13070
- {
13071
- $arguments = $command->getArguments();
13072
-
13073
- if ($this->checkSameSlotForKeys($arguments)) {
13074
- return $arguments[0];
13075
- }
13076
- }
13077
-
13078
- /**
13079
- * Extracts the key from a command with multiple keys only when all keys in
13080
- * the arguments array produce the same hash.
13081
- *
13082
- * @param CommandInterface $command Command instance.
13083
- *
13084
- * @return string|null
13085
- */
13086
- protected function getKeyFromInterleavedArguments(CommandInterface $command)
13087
- {
13088
- $arguments = $command->getArguments();
13089
- $keys = array();
13090
-
13091
- for ($i = 0; $i < count($arguments); $i += 2) {
13092
- $keys[] = $arguments[$i];
13093
- }
13094
-
13095
- if ($this->checkSameSlotForKeys($keys)) {
13096
- return $arguments[0];
13097
- }
13098
- }
13099
-
13100
- /**
13101
- * Extracts the key from SORT command.
13102
- *
13103
- * @param CommandInterface $command Command instance.
13104
- *
13105
- * @return string|null
13106
- */
13107
- protected function getKeyFromSortCommand(CommandInterface $command)
13108
- {
13109
- $arguments = $command->getArguments();
13110
- $firstKey = $arguments[0];
13111
-
13112
- if (1 === $argc = count($arguments)) {
13113
- return $firstKey;
13114
- }
13115
-
13116
- $keys = array($firstKey);
13117
-
13118
- for ($i = 1; $i < $argc; ++$i) {
13119
- if (strtoupper($arguments[$i]) === 'STORE') {
13120
- $keys[] = $arguments[++$i];
13121
- }
13122
- }
13123
-
13124
- if ($this->checkSameSlotForKeys($keys)) {
13125
- return $firstKey;
13126
- }
13127
- }
13128
-
13129
- /**
13130
- * Extracts the key from BLPOP and BRPOP commands.
13131
- *
13132
- * @param CommandInterface $command Command instance.
13133
- *
13134
- * @return string|null
13135
- */
13136
- protected function getKeyFromBlockingListCommands(CommandInterface $command)
13137
- {
13138
- $arguments = $command->getArguments();
13139
-
13140
- if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
13141
- return $arguments[0];
13142
- }
13143
- }
13144
-
13145
- /**
13146
- * Extracts the key from BITOP command.
13147
- *
13148
- * @param CommandInterface $command Command instance.
13149
- *
13150
- * @return string|null
13151
- */
13152
- protected function getKeyFromBitOp(CommandInterface $command)
13153
- {
13154
- $arguments = $command->getArguments();
13155
-
13156
- if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) {
13157
- return $arguments[1];
13158
- }
13159
- }
13160
-
13161
- /**
13162
- * Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
13163
- *
13164
- * @param CommandInterface $command Command instance.
13165
- *
13166
- * @return string|null
13167
- */
13168
- protected function getKeyFromGeoradiusCommands(CommandInterface $command)
13169
- {
13170
- $arguments = $command->getArguments();
13171
- $argc = count($arguments);
13172
- $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
13173
-
13174
- if ($argc > $startIndex) {
13175
- $keys = array($arguments[0]);
13176
-
13177
- for ($i = $startIndex; $i < $argc; ++$i) {
13178
- $argument = strtoupper($arguments[$i]);
13179
- if ($argument === 'STORE' || $argument === 'STOREDIST') {
13180
- $keys[] = $arguments[++$i];
13181
- }
13182
- }
13183
-
13184
- if ($this->checkSameSlotForKeys($keys)) {
13185
- return $arguments[0];
13186
- } else {
13187
- return;
13188
- }
13189
- }
13190
-
13191
- return $arguments[0];
13192
- }
13193
-
13194
- /**
13195
- * Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
13196
- *
13197
- * @param CommandInterface $command Command instance.
13198
- *
13199
- * @return string|null
13200
- */
13201
- protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
13202
- {
13203
- $arguments = $command->getArguments();
13204
- $keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1]));
13205
-
13206
- if ($this->checkSameSlotForKeys($keys)) {
13207
- return $arguments[0];
13208
- }
13209
- }
13210
-
13211
- /**
13212
- * Extracts the key from EVAL and EVALSHA commands.
13213
- *
13214
- * @param CommandInterface $command Command instance.
13215
- *
13216
- * @return string|null
13217
- */
13218
- protected function getKeyFromScriptingCommands(CommandInterface $command)
13219
- {
13220
- if ($command instanceof ScriptCommand) {
13221
- $keys = $command->getKeys();
13222
- } else {
13223
- $keys = array_slice($args = $command->getArguments(), 2, $args[1]);
13224
- }
13225
-
13226
- if ($keys && $this->checkSameSlotForKeys($keys)) {
13227
- return $keys[0];
13228
- }
13229
- }
13230
-
13231
- /**
13232
- * {@inheritdoc}
13233
- */
13234
- public function getSlot(CommandInterface $command)
13235
- {
13236
- $slot = $command->getSlot();
13237
-
13238
- if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) {
13239
- $key = call_user_func($this->commands[$cmdID], $command);
13240
-
13241
- if (isset($key)) {
13242
- $slot = $this->getSlotByKey($key);
13243
- $command->setSlot($slot);
13244
- }
13245
- }
13246
-
13247
- return $slot;
13248
- }
13249
-
13250
- /**
13251
- * Checks if the specified array of keys will generate the same hash.
13252
- *
13253
- * @param array $keys Array of keys.
13254
- *
13255
- * @return bool
13256
- */
13257
- protected function checkSameSlotForKeys(array $keys)
13258
- {
13259
- if (!$count = count($keys)) {
13260
- return false;
13261
- }
13262
-
13263
- $currentSlot = $this->getSlotByKey($keys[0]);
13264
-
13265
- for ($i = 1; $i < $count; ++$i) {
13266
- $nextSlot = $this->getSlotByKey($keys[$i]);
13267
-
13268
- if ($currentSlot !== $nextSlot) {
13269
- return false;
13270
- }
13271
-
13272
- $currentSlot = $nextSlot;
13273
- }
13274
-
13275
- return true;
13276
- }
13277
-
13278
- /**
13279
- * Returns only the hashable part of a key (delimited by "{...}"), or the
13280
- * whole key if a key tag is not found in the string.
13281
- *
13282
- * @param string $key A key.
13283
- *
13284
- * @return string
13285
- */
13286
- protected function extractKeyTag($key)
13287
- {
13288
- if (false !== $start = strpos($key, '{')) {
13289
- if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) {
13290
- $key = substr($key, $start, $end - $start);
13291
- }
13292
- }
13293
-
13294
- return $key;
13295
- }
13296
- }
13297
-
13298
- /**
13299
- * Default class used by Predis to calculate hashes out of keys of
13300
- * commands supported by redis-cluster.
13301
- *
13302
- * @author Daniele Alessandri <suppakilla@gmail.com>
13303
- */
13304
- class RedisStrategy extends ClusterStrategy
13305
- {
13306
- protected $hashGenerator;
13307
-
13308
- /**
13309
- * @param HashGeneratorInterface $hashGenerator Hash generator instance.
13310
- */
13311
- public function __construct(HashGeneratorInterface $hashGenerator = null)
13312
- {
13313
- parent::__construct();
13314
-
13315
- $this->hashGenerator = $hashGenerator ?: new CRC16();
13316
- }
13317
-
13318
- /**
13319
- * {@inheritdoc}
13320
- */
13321
- public function getSlotByKey($key)
13322
- {
13323
- $key = $this->extractKeyTag($key);
13324
- $slot = $this->hashGenerator->hash($key) & 0x3FFF;
13325
-
13326
- return $slot;
13327
- }
13328
-
13329
- /**
13330
- * {@inheritdoc}
13331
- */
13332
- public function getDistributor()
13333
- {
13334
- throw new NotSupportedException(
13335
- 'This cluster strategy does not provide an external distributor'
13336
- );
13337
- }
13338
- }
13339
-
13340
- /**
13341
- * Default cluster strategy used by Predis to handle client-side sharding.
13342
- *
13343
- * @author Daniele Alessandri <suppakilla@gmail.com>
13344
- */
13345
- class PredisStrategy extends ClusterStrategy
13346
- {
13347
- protected $distributor;
13348
-
13349
- /**
13350
- * @param DistributorInterface $distributor Optional distributor instance.
13351
- */
13352
- public function __construct(DistributorInterface $distributor = null)
13353
- {
13354
- parent::__construct();
13355
-
13356
- $this->distributor = $distributor ?: new HashRing();
13357
- }
13358
-
13359
- /**
13360
- * {@inheritdoc}
13361
- */
13362
- public function getSlotByKey($key)
13363
- {
13364
- $key = $this->extractKeyTag($key);
13365
- $hash = $this->distributor->hash($key);
13366
- $slot = $this->distributor->getSlot($hash);
13367
-
13368
- return $slot;
13369
- }
13370
-
13371
- /**
13372
- * {@inheritdoc}
13373
- */
13374
- protected function checkSameSlotForKeys(array $keys)
13375
- {
13376
- if (!$count = count($keys)) {
13377
- return false;
13378
- }
13379
-
13380
- $currentKey = $this->extractKeyTag($keys[0]);
13381
-
13382
- for ($i = 1; $i < $count; ++$i) {
13383
- $nextKey = $this->extractKeyTag($keys[$i]);
13384
-
13385
- if ($currentKey !== $nextKey) {
13386
- return false;
13387
- }
13388
-
13389
- $currentKey = $nextKey;
13390
- }
13391
-
13392
- return true;
13393
- }
13394
-
13395
- /**
13396
- * {@inheritdoc}
13397
- */
13398
- public function getDistributor()
13399
- {
13400
- return $this->distributor;
13401
- }
13402
- }
13403
-
13404
- /* --------------------------------------------------------------------------- */
13405
-
13406
- namespace Predis\Protocol;
13407
-
13408
- use Predis\Connection\CompositeConnectionInterface;
13409
- use Predis\Command\CommandInterface;
13410
- use Predis\CommunicationException;
13411
-
13412
- /**
13413
- * Defines a pluggable protocol processor capable of serializing commands and
13414
- * deserializing responses into PHP objects directly from a connection.
13415
- *
13416
- * @author Daniele Alessandri <suppakilla@gmail.com>
13417
- */
13418
- interface ProtocolProcessorInterface
13419
- {
13420
- /**
13421
- * Writes a request over a connection to Redis.
13422
- *
13423
- * @param CompositeConnectionInterface $connection Redis connection.
13424
- * @param CommandInterface $command Command instance.
13425
- */
13426
- public function write(CompositeConnectionInterface $connection, CommandInterface $command);
13427
-
13428
- /**
13429
- * Reads a response from a connection to Redis.
13430
- *
13431
- * @param CompositeConnectionInterface $connection Redis connection.
13432
- *
13433
- * @return mixed
13434
- */
13435
- public function read(CompositeConnectionInterface $connection);
13436
- }
13437
-
13438
- /**
13439
- * Defines a pluggable reader capable of parsing responses returned by Redis and
13440
- * deserializing them to PHP objects.
13441
- *
13442
- * @author Daniele Alessandri <suppakilla@gmail.com>
13443
- */
13444
- interface ResponseReaderInterface
13445
- {
13446
- /**
13447
- * Reads a response from a connection to Redis.
13448
- *
13449
- * @param CompositeConnectionInterface $connection Redis connection.
13450
- *
13451
- * @return mixed
13452
- */
13453
- public function read(CompositeConnectionInterface $connection);
13454
- }
13455
-
13456
- /**
13457
- * Defines a pluggable serializer for Redis commands.
13458
- *
13459
- * @author Daniele Alessandri <suppakilla@gmail.com>
13460
- */
13461
- interface RequestSerializerInterface
13462
- {
13463
- /**
13464
- * Serializes a Redis command.
13465
- *
13466
- * @param CommandInterface $command Redis command.
13467
- *
13468
- * @return string
13469
- */
13470
- public function serialize(CommandInterface $command);
13471
- }
13472
-
13473
- /**
13474
- * Exception used to indentify errors encountered while parsing the Redis wire
13475
- * protocol.
13476
- *
13477
- * @author Daniele Alessandri <suppakilla@gmail.com>
13478
- */
13479
- class ProtocolException extends CommunicationException
13480
- {
13481
- }
13482
-
13483
- /* --------------------------------------------------------------------------- */
13484
-
13485
- namespace Predis\Cluster\Distributor;
13486
-
13487
- use Predis\Cluster\Hash\HashGeneratorInterface;
13488
-
13489
- /**
13490
- * A distributor implements the logic to automatically distribute keys among
13491
- * several nodes for client-side sharding.
13492
- *
13493
- * @author Daniele Alessandri <suppakilla@gmail.com>
13494
- */
13495
- interface DistributorInterface
13496
- {
13497
- /**
13498
- * Adds a node to the distributor with an optional weight.
13499
- *
13500
- * @param mixed $node Node object.
13501
- * @param int $weight Weight for the node.
13502
- */
13503
- public function add($node, $weight = null);
13504
-
13505
- /**
13506
- * Removes a node from the distributor.
13507
- *
13508
- * @param mixed $node Node object.
13509
- */
13510
- public function remove($node);
13511
-
13512
- /**
13513
- * Returns the corresponding slot of a node from the distributor using the
13514
- * computed hash of a key.
13515
- *
13516
- * @param mixed $hash
13517
- *
13518
- * @return mixed
13519
- */
13520
- public function getSlot($hash);
13521
-
13522
- /**
13523
- * Returns a node from the distributor using its assigned slot ID.
13524
- *
13525
- * @param mixed $slot
13526
- *
13527
- * @return mixed|null
13528
- */
13529
- public function getBySlot($slot);
13530
-
13531
- /**
13532
- * Returns a node from the distributor using the computed hash of a key.
13533
- *
13534
- * @param mixed $hash
13535
- *
13536
- * @return mixed
13537
- */
13538
- public function getByHash($hash);
13539
-
13540
- /**
13541
- * Returns a node from the distributor mapping to the specified value.
13542
- *
13543
- * @param string $value
13544
- *
13545
- * @return mixed
13546
- */
13547
- public function get($value);
13548
-
13549
- /**
13550
- * Returns the underlying hash generator instance.
13551
- *
13552
- * @return HashGeneratorInterface
13553
- */
13554
- public function getHashGenerator();
13555
- }
13556
-
13557
- /**
13558
- * This class implements an hashring-based distributor that uses the same
13559
- * algorithm of memcache to distribute keys in a cluster using client-side
13560
- * sharding.
13561
- *
13562
- * @author Daniele Alessandri <suppakilla@gmail.com>
13563
- * @author Lorenzo Castelli <lcastelli@gmail.com>
13564
- */
13565
- class HashRing implements DistributorInterface, HashGeneratorInterface
13566
- {
13567
- const DEFAULT_REPLICAS = 128;
13568
- const DEFAULT_WEIGHT = 100;
13569
-
13570
- private $ring;
13571
- private $ringKeys;
13572
- private $ringKeysCount;
13573
- private $replicas;
13574
- private $nodeHashCallback;
13575
- private $nodes = array();
13576
-
13577
- /**
13578
- * @param int $replicas Number of replicas in the ring.
13579
- * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
13580
- */
13581
- public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
13582
- {
13583
- $this->replicas = $replicas;
13584
- $this->nodeHashCallback = $nodeHashCallback;
13585
- }
13586
-
13587
- /**
13588
- * Adds a node to the ring with an optional weight.
13589
- *
13590
- * @param mixed $node Node object.
13591
- * @param int $weight Weight for the node.
13592
- */
13593
- public function add($node, $weight = null)
13594
- {
13595
- // In case of collisions in the hashes of the nodes, the node added
13596
- // last wins, thus the order in which nodes are added is significant.
13597
- $this->nodes[] = array(
13598
- 'object' => $node,
13599
- 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
13600
- );
13601
-
13602
- $this->reset();
13603
- }
13604
-
13605
- /**
13606
- * {@inheritdoc}
13607
- */
13608
- public function remove($node)
13609
- {
13610
- // A node is removed by resetting the ring so that it's recreated from
13611
- // scratch, in order to reassign possible hashes with collisions to the
13612
- // right node according to the order in which they were added in the
13613
- // first place.
13614
- for ($i = 0; $i < count($this->nodes); ++$i) {
13615
- if ($this->nodes[$i]['object'] === $node) {
13616
- array_splice($this->nodes, $i, 1);
13617
- $this->reset();
13618
-
13619
- break;
13620
- }
13621
- }
13622
- }
13623
-
13624
- /**
13625
- * Resets the distributor.
13626
- */
13627
- private function reset()
13628
- {
13629
- unset(
13630
- $this->ring,
13631
- $this->ringKeys,
13632
- $this->ringKeysCount
13633
- );
13634
- }
13635
-
13636
- /**
13637
- * Returns the initialization status of the distributor.
13638
- *
13639
- * @return bool
13640
- */
13641
- private function isInitialized()
13642
- {
13643
- return isset($this->ringKeys);
13644
- }
13645
-
13646
- /**
13647
- * Calculates the total weight of all the nodes in the distributor.
13648
- *
13649
- * @return int
13650
- */
13651
- private function computeTotalWeight()
13652
- {
13653
- $totalWeight = 0;
13654
-
13655
- foreach ($this->nodes as $node) {
13656
- $totalWeight += $node['weight'];
13657
- }
13658
-
13659
- return $totalWeight;
13660
- }
13661
-
13662
- /**
13663
- * Initializes the distributor.
13664
- */
13665
- private function initialize()
13666
- {
13667
- if ($this->isInitialized()) {
13668
- return;
13669
- }
13670
-
13671
- if (!$this->nodes) {
13672
- throw new EmptyRingException('Cannot initialize an empty hashring.');
13673
- }
13674
-
13675
- $this->ring = array();
13676
- $totalWeight = $this->computeTotalWeight();
13677
- $nodesCount = count($this->nodes);
13678
-
13679
- foreach ($this->nodes as $node) {
13680
- $weightRatio = $node['weight'] / $totalWeight;
13681
- $this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
13682
- }
13683
-
13684
- ksort($this->ring, SORT_NUMERIC);
13685
- $this->ringKeys = array_keys($this->ring);
13686
- $this->ringKeysCount = count($this->ringKeys);
13687
- }
13688
-
13689
- /**
13690
- * Implements the logic needed to add a node to the hashring.
13691
- *
13692
- * @param array $ring Source hashring.
13693
- * @param mixed $node Node object to be added.
13694
- * @param int $totalNodes Total number of nodes.
13695
- * @param int $replicas Number of replicas in the ring.
13696
- * @param float $weightRatio Weight ratio for the node.
13697
- */
13698
- protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
13699
- {
13700
- $nodeObject = $node['object'];
13701
- $nodeHash = $this->getNodeHash($nodeObject);
13702
- $replicas = (int) round($weightRatio * $totalNodes * $replicas);
13703
-
13704
- for ($i = 0; $i < $replicas; ++$i) {
13705
- $key = crc32("$nodeHash:$i");
13706
- $ring[$key] = $nodeObject;
13707
- }
13708
- }
13709
-
13710
- /**
13711
- * {@inheritdoc}
13712
- */
13713
- protected function getNodeHash($nodeObject)
13714
- {
13715
- if (!isset($this->nodeHashCallback)) {
13716
- return (string) $nodeObject;
13717
- }
13718
-
13719
- return call_user_func($this->nodeHashCallback, $nodeObject);
13720
- }
13721
-
13722
- /**
13723
- * {@inheritdoc}
13724
- */
13725
- public function hash($value)
13726
- {
13727
- return crc32($value);
13728
- }
13729
-
13730
- /**
13731
- * {@inheritdoc}
13732
- */
13733
- public function getByHash($hash)
13734
- {
13735
- return $this->ring[$this->getSlot($hash)];
13736
- }
13737
-
13738
- /**
13739
- * {@inheritdoc}
13740
- */
13741
- public function getBySlot($slot)
13742
- {
13743
- $this->initialize();
13744
-
13745
- if (isset($this->ring[$slot])) {
13746
- return $this->ring[$slot];
13747
- }
13748
- }
13749
-
13750
- /**
13751
- * {@inheritdoc}
13752
- */
13753
- public function getSlot($hash)
13754
- {
13755
- $this->initialize();
13756
-
13757
- $ringKeys = $this->ringKeys;
13758
- $upper = $this->ringKeysCount - 1;
13759
- $lower = 0;
13760
-
13761
- while ($lower <= $upper) {
13762
- $index = ($lower + $upper) >> 1;
13763
- $item = $ringKeys[$index];
13764
-
13765
- if ($item > $hash) {
13766
- $upper = $index - 1;
13767
- } elseif ($item < $hash) {
13768
- $lower = $index + 1;
13769
- } else {
13770
- return $item;
13771
- }
13772
- }
13773
-
13774
- return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)];
13775
- }
13776
-
13777
- /**
13778
- * {@inheritdoc}
13779
- */
13780
- public function get($value)
13781
- {
13782
- $hash = $this->hash($value);
13783
- $node = $this->getByHash($hash);
13784
-
13785
- return $node;
13786
- }
13787
-
13788
- /**
13789
- * Implements a strategy to deal with wrap-around errors during binary searches.
13790
- *
13791
- * @param int $upper
13792
- * @param int $lower
13793
- * @param int $ringKeysCount
13794
- *
13795
- * @return int
13796
- */
13797
- protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
13798
- {
13799
- // Binary search for the last item in ringkeys with a value less or
13800
- // equal to the key. If no such item exists, return the last item.
13801
- return $upper >= 0 ? $upper : $ringKeysCount - 1;
13802
- }
13803
-
13804
- /**
13805
- * {@inheritdoc}
13806
- */
13807
- public function getHashGenerator()
13808
- {
13809
- return $this;
13810
- }
13811
- }
13812
-
13813
- /**
13814
- * Exception class that identifies empty rings.
13815
- *
13816
- * @author Daniele Alessandri <suppakilla@gmail.com>
13817
- */
13818
- class EmptyRingException extends \Exception
13819
- {
13820
- }
13821
-
13822
- /**
13823
- * This class implements an hashring-based distributor that uses the same
13824
- * algorithm of libketama to distribute keys in a cluster using client-side
13825
- * sharding.
13826
- *
13827
- * @author Daniele Alessandri <suppakilla@gmail.com>
13828
- * @author Lorenzo Castelli <lcastelli@gmail.com>
13829
- */
13830
- class KetamaRing extends HashRing
13831
- {
13832
- const DEFAULT_REPLICAS = 160;
13833
-
13834
- /**
13835
- * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
13836
- */
13837
- public function __construct($nodeHashCallback = null)
13838
- {
13839
- parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
13840
- }
13841
-
13842
- /**
13843
- * {@inheritdoc}
13844
- */
13845
- protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
13846
- {
13847
- $nodeObject = $node['object'];
13848
- $nodeHash = $this->getNodeHash($nodeObject);
13849
- $replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
13850
-
13851
- for ($i = 0; $i < $replicas; ++$i) {
13852
- $unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
13853
-
13854
- foreach ($unpackedDigest as $key) {
13855
- $ring[$key] = $nodeObject;
13856
- }
13857
- }
13858
- }
13859
-
13860
- /**
13861
- * {@inheritdoc}
13862
- */
13863
- public function hash($value)
13864
- {
13865
- $hash = unpack('V', md5($value, true));
13866
-
13867
- return $hash[1];
13868
- }
13869
-
13870
- /**
13871
- * {@inheritdoc}
13872
- */
13873
- protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
13874
- {
13875
- // Binary search for the first item in ringkeys with a value greater
13876
- // or equal to the key. If no such item exists, return the first item.
13877
- return $lower < $ringKeysCount ? $lower : 0;
13878
- }
13879
- }
13880
-
13881
- /* --------------------------------------------------------------------------- */
13882
-
13883
- namespace Predis\Pipeline;
13884
-
13885
- use Predis\Connection\ConnectionInterface;
13886
- use Predis\ClientException;
13887
- use Predis\ClientInterface;
13888
- use Predis\Connection\NodeConnectionInterface;
13889
- use Predis\Response\ErrorInterface as ErrorResponseInterface;
13890
- use Predis\Response\ResponseInterface;
13891
- use Predis\Response\ServerException;
13892
- use Predis\ClientContextInterface;
13893
- use Predis\Command\CommandInterface;
13894
- use Predis\Connection\Aggregate\ReplicationInterface;
13895
- use Predis\CommunicationException;
13896
- use Predis\Connection\Aggregate\ClusterInterface;
13897
- use Predis\NotSupportedException;
13898
-
13899
- /**
13900
- * Implementation of a command pipeline in which write and read operations of
13901
- * Redis commands are pipelined to alleviate the effects of network round-trips.
13902
- *
13903
- * {@inheritdoc}
13904
- *
13905
- * @author Daniele Alessandri <suppakilla@gmail.com>
13906
- */
13907
- class Pipeline implements ClientContextInterface
13908
- {
13909
- private $client;
13910
- private $pipeline;
13911
-
13912
- private $responses = array();
13913
- private $running = false;
13914
-
13915
- /**
13916
- * @param ClientInterface $client Client instance used by the context.
13917
- */
13918
- public function __construct(ClientInterface $client)
13919
- {
13920
- $this->client = $client;
13921
- $this->pipeline = new \SplQueue();
13922
- }
13923
-
13924
- /**
13925
- * Queues a command into the pipeline buffer.
13926
- *
13927
- * @param string $method Command ID.
13928
- * @param array $arguments Arguments for the command.
13929
- *
13930
- * @return $this
13931
- */
13932
- public function __call($method, $arguments)
13933
- {
13934
- $command = $this->client->createCommand($method, $arguments);
13935
- $this->recordCommand($command);
13936
-
13937
- return $this;
13938
- }
13939
-
13940
- /**
13941
- * Queues a command instance into the pipeline buffer.
13942
- *
13943
- * @param CommandInterface $command Command to be queued in the buffer.
13944
- */
13945
- protected function recordCommand(CommandInterface $command)
13946
- {
13947
- $this->pipeline->enqueue($command);
13948
- }
13949
-
13950
- /**
13951
- * Queues a command instance into the pipeline buffer.
13952
- *
13953
- * @param CommandInterface $command Command instance to be queued in the buffer.
13954
- *
13955
- * @return $this
13956
- */
13957
- public function executeCommand(CommandInterface $command)
13958
- {
13959
- $this->recordCommand($command);
13960
-
13961
- return $this;
13962
- }
13963
-
13964
- /**
13965
- * Throws an exception on -ERR responses returned by Redis.
13966
- *
13967
- * @param ConnectionInterface $connection Redis connection that returned the error.
13968
- * @param ErrorResponseInterface $response Instance of the error response.
13969
- *
13970
- * @throws ServerException
13971
- */
13972
- protected function exception(ConnectionInterface $connection, ErrorResponseInterface $response)
13973
- {
13974
- $connection->disconnect();
13975
- $message = $response->getMessage();
13976
-
13977
- throw new ServerException($message);
13978
- }
13979
-
13980
- /**
13981
- * Returns the underlying connection to be used by the pipeline.
13982
- *
13983
- * @return ConnectionInterface
13984
- */
13985
- protected function getConnection()
13986
- {
13987
- $connection = $this->getClient()->getConnection();
13988
-
13989
- if ($connection instanceof ReplicationInterface) {
13990
- $connection->switchTo('master');
13991
- }
13992
-
13993
- return $connection;
13994
- }
13995
-
13996
- /**
13997
- * Implements the logic to flush the queued commands and read the responses
13998
- * from the current connection.
13999
- *
14000
- * @param ConnectionInterface $connection Current connection instance.
14001
- * @param \SplQueue $commands Queued commands.
14002
- *
14003
- * @return array
14004
- */
14005
- protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
14006
- {
14007
- foreach ($commands as $command) {
14008
- $connection->writeRequest($command);
14009
- }
14010
-
14011
- $responses = array();
14012
- $exceptions = $this->throwServerExceptions();
14013
-
14014
- while (!$commands->isEmpty()) {
14015
- $command = $commands->dequeue();
14016
- $response = $connection->readResponse($command);
14017
-
14018
- if (!$response instanceof ResponseInterface) {
14019
- $responses[] = $command->parseResponse($response);
14020
- } elseif ($response instanceof ErrorResponseInterface && $exceptions) {
14021
- $this->exception($connection, $response);
14022
- } else {
14023
- $responses[] = $response;
14024
- }
14025
- }
14026
-
14027
- return $responses;
14028
- }
14029
-
14030
- /**
14031
- * Flushes the buffer holding all of the commands queued so far.
14032
- *
14033
- * @param bool $send Specifies if the commands in the buffer should be sent to Redis.
14034
- *
14035
- * @return $this
14036
- */
14037
- public function flushPipeline($send = true)
14038
- {
14039
- if ($send && !$this->pipeline->isEmpty()) {
14040
- $responses = $this->executePipeline($this->getConnection(), $this->pipeline);
14041
- $this->responses = array_merge($this->responses, $responses);
14042
- } else {
14043
- $this->pipeline = new \SplQueue();
14044
- }
14045
-
14046
- return $this;
14047
- }
14048
-
14049
- /**
14050
- * Marks the running status of the pipeline.
14051
- *
14052
- * @param bool $bool Sets the running status of the pipeline.
14053
- *
14054
- * @throws ClientException
14055
- */
14056
- private function setRunning($bool)
14057
- {
14058
- if ($bool && $this->running) {
14059
- throw new ClientException('The current pipeline context is already being executed.');
14060
- }
14061
-
14062
- $this->running = $bool;
14063
- }
14064
-
14065
- /**
14066
- * Handles the actual execution of the whole pipeline.
14067
- *
14068
- * @param mixed $callable Optional callback for execution.
14069
- *
14070
- * @throws \Exception
14071
- * @throws \InvalidArgumentException
14072
- *
14073
- * @return array
14074
- */
14075
- public function execute($callable = null)
14076
- {
14077
- if ($callable && !is_callable($callable)) {
14078
- throw new \InvalidArgumentException('The argument must be a callable object.');
14079
- }
14080
-
14081
- $exception = null;
14082
- $this->setRunning(true);
14083
-
14084
- try {
14085
- if ($callable) {
14086
- call_user_func($callable, $this);
14087
- }
14088
-
14089
- $this->flushPipeline();
14090
- } catch (\Exception $exception) {
14091
- // NOOP
14092
- }
14093
-
14094
- $this->setRunning(false);
14095
-
14096
- if ($exception) {
14097
- throw $exception;
14098
- }
14099
-
14100
- return $this->responses;
14101
- }
14102
-
14103
- /**
14104
- * Returns if the pipeline should throw exceptions on server errors.
14105
- *
14106
- * @return bool
14107
- */
14108
- protected function throwServerExceptions()
14109
- {
14110
- return (bool) $this->client->getOptions()->exceptions;
14111
- }
14112
-
14113
- /**
14114
- * Returns the underlying client instance used by the pipeline object.
14115
- *
14116
- * @return ClientInterface
14117
- */
14118
- public function getClient()
14119
- {
14120
- return $this->client;
14121
- }
14122
- }
14123
-
14124
- /**
14125
- * Command pipeline that writes commands to the servers but discards responses.
14126
- *
14127
- * @author Daniele Alessandri <suppakilla@gmail.com>
14128
- */
14129
- class FireAndForget extends Pipeline
14130
- {
14131
- /**
14132
- * {@inheritdoc}
14133
- */
14134
- protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
14135
- {
14136
- while (!$commands->isEmpty()) {
14137
- $connection->writeRequest($commands->dequeue());
14138
- }
14139
-
14140
- $connection->disconnect();
14141
-
14142
- return array();
14143
- }
14144
- }
14145
-
14146
- /**
14147
- * Command pipeline wrapped into a MULTI / EXEC transaction.
14148
- *
14149
- * @author Daniele Alessandri <suppakilla@gmail.com>
14150
- */
14151
- class Atomic extends Pipeline
14152
- {
14153
- /**
14154
- * {@inheritdoc}
14155
- */
14156
- public function __construct(ClientInterface $client)
14157
- {
14158
- if (!$client->getProfile()->supportsCommands(array('multi', 'exec', 'discard'))) {
14159
- throw new ClientException(
14160
- "The current profile does not support 'MULTI', 'EXEC' and 'DISCARD'."
14161
- );
14162
- }
14163
-
14164
- parent::__construct($client);
14165
- }
14166
-
14167
- /**
14168
- * {@inheritdoc}
14169
- */
14170
- protected function getConnection()
14171
- {
14172
- $connection = $this->getClient()->getConnection();
14173
-
14174
- if (!$connection instanceof NodeConnectionInterface) {
14175
- $class = __CLASS__;
14176
-
14177
- throw new ClientException("The class '$class' does not support aggregate connections.");
14178
- }
14179
-
14180
- return $connection;
14181
- }
14182
-
14183
- /**
14184
- * {@inheritdoc}
14185
- */
14186
- protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
14187
- {
14188
- $profile = $this->getClient()->getProfile();
14189
- $connection->executeCommand($profile->createCommand('multi'));
14190
-
14191
- foreach ($commands as $command) {
14192
- $connection->writeRequest($command);
14193
- }
14194
-
14195
- foreach ($commands as $command) {
14196
- $response = $connection->readResponse($command);
14197
-
14198
- if ($response instanceof ErrorResponseInterface) {
14199
- $connection->executeCommand($profile->createCommand('discard'));
14200
- throw new ServerException($response->getMessage());
14201
- }
14202
- }
14203
-
14204
- $executed = $connection->executeCommand($profile->createCommand('exec'));
14205
-
14206
- if (!isset($executed)) {
14207
- // TODO: should be throwing a more appropriate exception.
14208
- throw new ClientException(
14209
- 'The underlying transaction has been aborted by the server.'
14210
- );
14211
- }
14212
-
14213
- if (count($executed) !== count($commands)) {
14214
- $expected = count($commands);
14215
- $received = count($executed);
14216
-
14217
- throw new ClientException(
14218
- "Invalid number of responses [expected $expected, received $received]."
14219
- );
14220
- }
14221
-
14222
- $responses = array();
14223
- $sizeOfPipe = count($commands);
14224
- $exceptions = $this->throwServerExceptions();
14225
-
14226
- for ($i = 0; $i < $sizeOfPipe; ++$i) {
14227
- $command = $commands->dequeue();
14228
- $response = $executed[$i];
14229
-
14230
- if (!$response instanceof ResponseInterface) {
14231
- $responses[] = $command->parseResponse($response);
14232
- } elseif ($response instanceof ErrorResponseInterface && $exceptions) {
14233
- $this->exception($connection, $response);
14234
- } else {
14235
- $responses[] = $response;
14236
- }
14237
-
14238
- unset($executed[$i]);
14239
- }
14240
-
14241
- return $responses;
14242
- }
14243
- }
14244
-
14245
- /**
14246
- * Command pipeline that does not throw exceptions on connection errors, but
14247
- * returns the exception instances as the rest of the response elements.
14248
- *
14249
- * @todo Awful naming!
14250
- *
14251
- * @author Daniele Alessandri <suppakilla@gmail.com>
14252
- */
14253
- class ConnectionErrorProof extends Pipeline
14254
- {
14255
- /**
14256
- * {@inheritdoc}
14257
- */
14258
- protected function getConnection()
14259
- {
14260
- return $this->getClient()->getConnection();
14261
- }
14262
-
14263
- /**
14264
- * {@inheritdoc}
14265
- */
14266
- protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
14267
- {
14268
- if ($connection instanceof NodeConnectionInterface) {
14269
- return $this->executeSingleNode($connection, $commands);
14270
- } elseif ($connection instanceof ClusterInterface) {
14271
- return $this->executeCluster($connection, $commands);
14272
- } else {
14273
- $class = get_class($connection);
14274
-
14275
- throw new NotSupportedException("The connection class '$class' is not supported.");
14276
- }
14277
- }
14278
-
14279
- /**
14280
- * {@inheritdoc}
14281
- */
14282
- protected function executeSingleNode(NodeConnectionInterface $connection, \SplQueue $commands)
14283
- {
14284
- $responses = array();
14285
- $sizeOfPipe = count($commands);
14286
-
14287
- foreach ($commands as $command) {
14288
- try {
14289
- $connection->writeRequest($command);
14290
- } catch (CommunicationException $exception) {
14291
- return array_fill(0, $sizeOfPipe, $exception);
14292
- }
14293
- }
14294
-
14295
- for ($i = 0; $i < $sizeOfPipe; ++$i) {
14296
- $command = $commands->dequeue();
14297
-
14298
- try {
14299
- $responses[$i] = $connection->readResponse($command);
14300
- } catch (CommunicationException $exception) {
14301
- $add = count($commands) - count($responses);
14302
- $responses = array_merge($responses, array_fill(0, $add, $exception));
14303
-
14304
- break;
14305
- }
14306
- }
14307
-
14308
- return $responses;
14309
- }
14310
-
14311
- /**
14312
- * {@inheritdoc}
14313
- */
14314
- protected function executeCluster(ClusterInterface $connection, \SplQueue $commands)
14315
- {
14316
- $responses = array();
14317
- $sizeOfPipe = count($commands);
14318
- $exceptions = array();
14319
-
14320
- foreach ($commands as $command) {
14321
- $cmdConnection = $connection->getConnection($command);
14322
-
14323
- if (isset($exceptions[spl_object_hash($cmdConnection)])) {
14324
- continue;
14325
- }
14326
-
14327
- try {
14328
- $cmdConnection->writeRequest($command);
14329
- } catch (CommunicationException $exception) {
14330
- $exceptions[spl_object_hash($cmdConnection)] = $exception;
14331
- }
14332
- }
14333
-
14334
- for ($i = 0; $i < $sizeOfPipe; ++$i) {
14335
- $command = $commands->dequeue();
14336
-
14337
- $cmdConnection = $connection->getConnection($command);
14338
- $connectionHash = spl_object_hash($cmdConnection);
14339
-
14340
- if (isset($exceptions[$connectionHash])) {
14341
- $responses[$i] = $exceptions[$connectionHash];
14342
- continue;
14343
- }
14344
-
14345
- try {
14346
- $responses[$i] = $cmdConnection->readResponse($command);
14347
- } catch (CommunicationException $exception) {
14348
- $responses[$i] = $exception;
14349
- $exceptions[$connectionHash] = $exception;
14350
- }
14351
- }
14352
-
14353
- return $responses;
14354
- }
14355
- }
14356
-
14357
- /* --------------------------------------------------------------------------- */
14358
-
14359
- namespace Predis\Response\Iterator;
14360
-
14361
- use Predis\Connection\NodeConnectionInterface;
14362
- use Predis\Response\ResponseInterface;
14363
-
14364
- /**
14365
- * Iterator that abstracts the access to multibulk responses allowing them to be
14366
- * consumed in a streamable fashion without keeping the whole payload in memory.
14367
- *
14368
- * This iterator does not support rewinding which means that the iteration, once
14369
- * consumed, cannot be restarted.
14370
- *
14371
- * Always make sure that the whole iteration is consumed (or dropped) to prevent
14372
- * protocol desynchronization issues.
14373
- *
14374
- * @author Daniele Alessandri <suppakilla@gmail.com>
14375
- */
14376
- abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInterface
14377
- {
14378
- protected $current;
14379
- protected $position;
14380
- protected $size;
14381
-
14382
- /**
14383
- * {@inheritdoc}
14384
- */
14385
- public function rewind()
14386
- {
14387
- // NOOP
14388
- }
14389
-
14390
- /**
14391
- * {@inheritdoc}
14392
- */
14393
- public function current()
14394
- {
14395
- return $this->current;
14396
- }
14397
-
14398
- /**
14399
- * {@inheritdoc}
14400
- */
14401
- public function key()
14402
- {
14403
- return $this->position;
14404
- }
14405
-
14406
- /**
14407
- * {@inheritdoc}
14408
- */
14409
- public function next()
14410
- {
14411
- if (++$this->position < $this->size) {
14412
- $this->current = $this->getValue();
14413
- }
14414
- }
14415
-
14416
- /**
14417
- * {@inheritdoc}
14418
- */
14419
- public function valid()
14420
- {
14421
- return $this->position < $this->size;
14422
- }
14423
-
14424
- /**
14425
- * Returns the number of items comprising the whole multibulk response.
14426
- *
14427
- * This method should be used instead of iterator_count() to get the size of
14428
- * the current multibulk response since the former consumes the iteration to
14429
- * count the number of elements, but our iterators do not support rewinding.
14430
- *
14431
- * @return int
14432
- */
14433
- public function count()
14434
- {
14435
- return $this->size;
14436
- }
14437
-
14438
- /**
14439
- * Returns the current position of the iterator.
14440
- *
14441
- * @return int
14442
- */
14443
- public function getPosition()
14444
- {
14445
- return $this->position;
14446
- }
14447
-
14448
- /**
14449
- * {@inheritdoc}
14450
- */
14451
- abstract protected function getValue();
14452
- }
14453
-
14454
- /**
14455
- * Streamable multibulk response.
14456
- *
14457
- * @author Daniele Alessandri <suppakilla@gmail.com>
14458
- */
14459
- class MultiBulk extends MultiBulkIterator
14460
- {
14461
- private $connection;
14462
-
14463
- /**
14464
- * @param NodeConnectionInterface $connection Connection to Redis.
14465
- * @param int $size Number of elements of the multibulk response.
14466
- */
14467
- public function __construct(NodeConnectionInterface $connection, $size)
14468
- {
14469
- $this->connection = $connection;
14470
- $this->size = $size;
14471
- $this->position = 0;
14472
- $this->current = $size > 0 ? $this->getValue() : null;
14473
- }
14474
-
14475
- /**
14476
- * Handles the synchronization of the client with the Redis protocol when
14477
- * the garbage collector kicks in (e.g. when the iterator goes out of the
14478
- * scope of a foreach or it is unset).
14479
- */
14480
- public function __destruct()
14481
- {
14482
- $this->drop(true);
14483
- }
14484
-
14485
- /**
14486
- * Drop queued elements that have not been read from the connection either
14487
- * by consuming the rest of the multibulk response or quickly by closing the
14488
- * underlying connection.
14489
- *
14490
- * @param bool $disconnect Consume the iterator or drop the connection.
14491
- */
14492
- public function drop($disconnect = false)
14493
- {
14494
- if ($disconnect) {
14495
- if ($this->valid()) {
14496
- $this->position = $this->size;
14497
- $this->connection->disconnect();
14498
- }
14499
- } else {
14500
- while ($this->valid()) {
14501
- $this->next();
14502
- }
14503
- }
14504
- }
14505
-
14506
- /**
14507
- * Reads the next item of the multibulk response from the connection.
14508
- *
14509
- * @return mixed
14510
- */
14511
- protected function getValue()
14512
- {
14513
- return $this->connection->read();
14514
- }
14515
- }
14516
-
14517
- /**
14518
- * Outer iterator consuming streamable multibulk responses by yielding tuples of
14519
- * keys and values.
14520
- *
14521
- * This wrapper is useful for responses to commands such as `HGETALL` that can
14522
- * be iterater as $key => $value pairs.
14523
- *
14524
- * @author Daniele Alessandri <suppakilla@gmail.com>
14525
- */
14526
- class MultiBulkTuple extends MultiBulk implements \OuterIterator
14527
- {
14528
- private $iterator;
14529
-
14530
- /**
14531
- * @param MultiBulk $iterator Inner multibulk response iterator.
14532
- */
14533
- public function __construct(MultiBulk $iterator)
14534
- {
14535
- $this->checkPreconditions($iterator);
14536
-
14537
- $this->size = count($iterator) / 2;
14538
- $this->iterator = $iterator;
14539
- $this->position = $iterator->getPosition();
14540
- $this->current = $this->size > 0 ? $this->getValue() : null;
14541
- }
14542
-
14543
- /**
14544
- * Checks for valid preconditions.
14545
- *
14546
- * @param MultiBulk $iterator Inner multibulk response iterator.
14547
- *
14548
- * @throws \InvalidArgumentException
14549
- * @throws \UnexpectedValueException
14550
- */
14551
- protected function checkPreconditions(MultiBulk $iterator)
14552
- {
14553
- if ($iterator->getPosition() !== 0) {
14554
- throw new \InvalidArgumentException(
14555
- 'Cannot initialize a tuple iterator using an already initiated iterator.'
14556
- );
14557
- }
14558
-
14559
- if (($size = count($iterator)) % 2 !== 0) {
14560
- throw new \UnexpectedValueException('Invalid response size for a tuple iterator.');
14561
- }
14562
- }
14563
-
14564
- /**
14565
- * {@inheritdoc}
14566
- */
14567
- public function getInnerIterator()
14568
- {
14569
- return $this->iterator;
14570
- }
14571
-
14572
- /**
14573
- * {@inheritdoc}
14574
- */
14575
- public function __destruct()
14576
- {
14577
- $this->iterator->drop(true);
14578
- }
14579
-
14580
- /**
14581
- * {@inheritdoc}
14582
- */
14583
- protected function getValue()
14584
- {
14585
- $k = $this->iterator->current();
14586
- $this->iterator->next();
14587
-
14588
- $v = $this->iterator->current();
14589
- $this->iterator->next();
14590
-
14591
- return array($k, $v);
14592
- }
14593
- }
14594
-
14595
- /* --------------------------------------------------------------------------- */
14596
-
14597
- namespace Predis\Command\Processor;
14598
-
14599
- use Predis\Command\CommandInterface;
14600
- use Predis\Command\PrefixableCommandInterface;
14601
-
14602
- /**
14603
- * A command processor processes Redis commands before they are sent to Redis.
14604
- *
14605
- * @author Daniele Alessandri <suppakilla@gmail.com>
14606
- */
14607
- interface ProcessorInterface
14608
- {
14609
- /**
14610
- * Processes the given Redis command.
14611
- *
14612
- * @param CommandInterface $command Command instance.
14613
- */
14614
- public function process(CommandInterface $command);
14615
- }
14616
-
14617
- /**
14618
- * Command processor capable of prefixing keys stored in the arguments of Redis
14619
- * commands supported.
14620
- *
14621
- * @author Daniele Alessandri <suppakilla@gmail.com>
14622
- */
14623
- class KeyPrefixProcessor implements ProcessorInterface
14624
- {
14625
- private $prefix;
14626
- private $commands;
14627
-
14628
- /**
14629
- * @param string $prefix Prefix for the keys.
14630
- */
14631
- public function __construct($prefix)
14632
- {
14633
- $this->prefix = $prefix;
14634
- $this->commands = array(
14635
- /* ---------------- Redis 1.2 ---------------- */
14636
- 'EXISTS' => 'static::all',
14637
- 'DEL' => 'static::all',
14638
- 'TYPE' => 'static::first',
14639
- 'KEYS' => 'static::first',
14640
- 'RENAME' => 'static::all',
14641
- 'RENAMENX' => 'static::all',
14642
- 'EXPIRE' => 'static::first',
14643
- 'EXPIREAT' => 'static::first',
14644
- 'TTL' => 'static::first',
14645
- 'MOVE' => 'static::first',
14646
- 'SORT' => 'static::sort',
14647
- 'DUMP' => 'static::first',
14648
- 'RESTORE' => 'static::first',
14649
- 'SET' => 'static::first',
14650
- 'SETNX' => 'static::first',
14651
- 'MSET' => 'static::interleaved',
14652
- 'MSETNX' => 'static::interleaved',
14653
- 'GET' => 'static::first',
14654
- 'MGET' => 'static::all',
14655
- 'GETSET' => 'static::first',
14656
- 'INCR' => 'static::first',
14657
- 'INCRBY' => 'static::first',
14658
- 'DECR' => 'static::first',
14659
- 'DECRBY' => 'static::first',
14660
- 'RPUSH' => 'static::first',
14661
- 'LPUSH' => 'static::first',
14662
- 'LLEN' => 'static::first',
14663
- 'LRANGE' => 'static::first',
14664
- 'LTRIM' => 'static::first',
14665
- 'LINDEX' => 'static::first',
14666
- 'LSET' => 'static::first',
14667
- 'LREM' => 'static::first',
14668
- 'LPOP' => 'static::first',
14669
- 'RPOP' => 'static::first',
14670
- 'RPOPLPUSH' => 'static::all',
14671
- 'SADD' => 'static::first',
14672
- 'SREM' => 'static::first',
14673
- 'SPOP' => 'static::first',
14674
- 'SMOVE' => 'static::skipLast',
14675
- 'SCARD' => 'static::first',
14676
- 'SISMEMBER' => 'static::first',
14677
- 'SINTER' => 'static::all',
14678
- 'SINTERSTORE' => 'static::all',
14679
- 'SUNION' => 'static::all',
14680
- 'SUNIONSTORE' => 'static::all',
14681
- 'SDIFF' => 'static::all',
14682
- 'SDIFFSTORE' => 'static::all',
14683
- 'SMEMBERS' => 'static::first',
14684
- 'SRANDMEMBER' => 'static::first',
14685
- 'ZADD' => 'static::first',
14686
- 'ZINCRBY' => 'static::first',
14687
- 'ZREM' => 'static::first',
14688
- 'ZRANGE' => 'static::first',
14689
- 'ZREVRANGE' => 'static::first',
14690
- 'ZRANGEBYSCORE' => 'static::first',
14691
- 'ZCARD' => 'static::first',
14692
- 'ZSCORE' => 'static::first',
14693
- 'ZREMRANGEBYSCORE' => 'static::first',
14694
- /* ---------------- Redis 2.0 ---------------- */
14695
- 'SETEX' => 'static::first',
14696
- 'APPEND' => 'static::first',
14697
- 'SUBSTR' => 'static::first',
14698
- 'BLPOP' => 'static::skipLast',
14699
- 'BRPOP' => 'static::skipLast',
14700
- 'ZUNIONSTORE' => 'static::zsetStore',
14701
- 'ZINTERSTORE' => 'static::zsetStore',
14702
- 'ZCOUNT' => 'static::first',
14703
- 'ZRANK' => 'static::first',
14704
- 'ZREVRANK' => 'static::first',
14705
- 'ZREMRANGEBYRANK' => 'static::first',
14706
- 'HSET' => 'static::first',
14707
- 'HSETNX' => 'static::first',
14708
- 'HMSET' => 'static::first',
14709
- 'HINCRBY' => 'static::first',
14710
- 'HGET' => 'static::first',
14711
- 'HMGET' => 'static::first',
14712
- 'HDEL' => 'static::first',
14713
- 'HEXISTS' => 'static::first',
14714
- 'HLEN' => 'static::first',
14715
- 'HKEYS' => 'static::first',
14716
- 'HVALS' => 'static::first',
14717
- 'HGETALL' => 'static::first',
14718
- 'SUBSCRIBE' => 'static::all',
14719
- 'UNSUBSCRIBE' => 'static::all',
14720
- 'PSUBSCRIBE' => 'static::all',
14721
- 'PUNSUBSCRIBE' => 'static::all',
14722
- 'PUBLISH' => 'static::first',
14723
- /* ---------------- Redis 2.2 ---------------- */
14724
- 'PERSIST' => 'static::first',
14725
- 'STRLEN' => 'static::first',
14726
- 'SETRANGE' => 'static::first',
14727
- 'GETRANGE' => 'static::first',
14728
- 'SETBIT' => 'static::first',
14729
- 'GETBIT' => 'static::first',
14730
- 'RPUSHX' => 'static::first',
14731
- 'LPUSHX' => 'static::first',
14732
- 'LINSERT' => 'static::first',
14733
- 'BRPOPLPUSH' => 'static::skipLast',
14734
- 'ZREVRANGEBYSCORE' => 'static::first',
14735
- 'WATCH' => 'static::all',
14736
- /* ---------------- Redis 2.6 ---------------- */
14737
- 'PTTL' => 'static::first',
14738
- 'PEXPIRE' => 'static::first',
14739
- 'PEXPIREAT' => 'static::first',
14740
- 'PSETEX' => 'static::first',
14741
- 'INCRBYFLOAT' => 'static::first',
14742
- 'BITOP' => 'static::skipFirst',
14743
- 'BITCOUNT' => 'static::first',
14744
- 'HINCRBYFLOAT' => 'static::first',
14745
- 'EVAL' => 'static::evalKeys',
14746
- 'EVALSHA' => 'static::evalKeys',
14747
- 'MIGRATE' => 'static::migrate',
14748
- /* ---------------- Redis 2.8 ---------------- */
14749
- 'SSCAN' => 'static::first',
14750
- 'ZSCAN' => 'static::first',
14751
- 'HSCAN' => 'static::first',
14752
- 'PFADD' => 'static::first',
14753
- 'PFCOUNT' => 'static::all',
14754
- 'PFMERGE' => 'static::all',
14755
- 'ZLEXCOUNT' => 'static::first',
14756
- 'ZRANGEBYLEX' => 'static::first',
14757
- 'ZREMRANGEBYLEX' => 'static::first',
14758
- 'ZREVRANGEBYLEX' => 'static::first',
14759
- 'BITPOS' => 'static::first',
14760
- /* ---------------- Redis 3.2 ---------------- */
14761
- 'HSTRLEN' => 'static::first',
14762
- 'BITFIELD' => 'static::first',
14763
- 'GEOADD' => 'static::first',
14764
- 'GEOHASH' => 'static::first',
14765
- 'GEOPOS' => 'static::first',
14766
- 'GEODIST' => 'static::first',
14767
- 'GEORADIUS' => 'static::georadius',
14768
- 'GEORADIUSBYMEMBER' => 'static::georadius',
14769
- );
14770
- }
14771
-
14772
- /**
14773
- * Sets a prefix that is applied to all the keys.
14774
- *
14775
- * @param string $prefix Prefix for the keys.
14776
- */
14777
- public function setPrefix($prefix)
14778
- {
14779
- $this->prefix = $prefix;
14780
- }
14781
-
14782
- /**
14783
- * Gets the current prefix.
14784
- *
14785
- * @return string
14786
- */
14787
- public function getPrefix()
14788
- {
14789
- return $this->prefix;
14790
- }
14791
-
14792
- /**
14793
- * {@inheritdoc}
14794
- */
14795
- public function process(CommandInterface $command)
14796
- {
14797
- if ($command instanceof PrefixableCommandInterface) {
14798
- $command->prefixKeys($this->prefix);
14799
- } elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
14800
- call_user_func($this->commands[$commandID], $command, $this->prefix);
14801
- }
14802
- }
14803
-
14804
- /**
14805
- * Sets an handler for the specified command ID.
14806
- *
14807
- * The callback signature must have 2 parameters of the following types:
14808
- *
14809
- * - Predis\Command\CommandInterface (command instance)
14810
- * - String (prefix)
14811
- *
14812
- * When the callback argument is omitted or NULL, the previously
14813
- * associated handler for the specified command ID is removed.
14814
- *
14815
- * @param string $commandID The ID of the command to be handled.
14816
- * @param mixed $callback A valid callable object or NULL.
14817
- *
14818
- * @throws \InvalidArgumentException
14819
- */
14820
- public function setCommandHandler($commandID, $callback = null)
14821
- {
14822
- $commandID = strtoupper($commandID);
14823
-
14824
- if (!isset($callback)) {
14825
- unset($this->commands[$commandID]);
14826
-
14827
- return;
14828
- }
14829
-
14830
- if (!is_callable($callback)) {
14831
- throw new \InvalidArgumentException(
14832
- 'Callback must be a valid callable object or NULL'
14833
- );
14834
- }
14835
-
14836
- $this->commands[$commandID] = $callback;
14837
- }
14838
-
14839
- /**
14840
- * {@inheritdoc}
14841
- */
14842
- public function __toString()
14843
- {
14844
- return $this->getPrefix();
14845
- }
14846
-
14847
- /**
14848
- * Applies the specified prefix only the first argument.
14849
- *
14850
- * @param CommandInterface $command Command instance.
14851
- * @param string $prefix Prefix string.
14852
- */
14853
- public static function first(CommandInterface $command, $prefix)
14854
- {
14855
- if ($arguments = $command->getArguments()) {
14856
- $arguments[0] = "$prefix{$arguments[0]}";
14857
- $command->setRawArguments($arguments);
14858
- }
14859
- }
14860
-
14861
- /**
14862
- * Applies the specified prefix to all the arguments.
14863
- *
14864
- * @param CommandInterface $command Command instance.
14865
- * @param string $prefix Prefix string.
14866
- */
14867
- public static function all(CommandInterface $command, $prefix)
14868
- {
14869
- if ($arguments = $command->getArguments()) {
14870
- foreach ($arguments as &$key) {
14871
- $key = "$prefix$key";
14872
- }
14873
-
14874
- $command->setRawArguments($arguments);
14875
- }
14876
- }
14877
-
14878
- /**
14879
- * Applies the specified prefix only to even arguments in the list.
14880
- *
14881
- * @param CommandInterface $command Command instance.
14882
- * @param string $prefix Prefix string.
14883
- */
14884
- public static function interleaved(CommandInterface $command, $prefix)
14885
- {
14886
- if ($arguments = $command->getArguments()) {
14887
- $length = count($arguments);
14888
-
14889
- for ($i = 0; $i < $length; $i += 2) {
14890
- $arguments[$i] = "$prefix{$arguments[$i]}";
14891
- }
14892
-
14893
- $command->setRawArguments($arguments);
14894
- }
14895
- }
14896
-
14897
- /**
14898
- * Applies the specified prefix to all the arguments but the first one.
14899
- *
14900
- * @param CommandInterface $command Command instance.
14901
- * @param string $prefix Prefix string.
14902
- */
14903
- public static function skipFirst(CommandInterface $command, $prefix)
14904
- {
14905
- if ($arguments = $command->getArguments()) {
14906
- $length = count($arguments);
14907
-
14908
- for ($i = 1; $i < $length; ++$i) {
14909
- $arguments[$i] = "$prefix{$arguments[$i]}";
14910
- }
14911
-
14912
- $command->setRawArguments($arguments);
14913
- }
14914
- }
14915
-
14916
- /**
14917
- * Applies the specified prefix to all the arguments but the last one.
14918
- *
14919
- * @param CommandInterface $command Command instance.
14920
- * @param string $prefix Prefix string.
14921
- */
14922
- public static function skipLast(CommandInterface $command, $prefix)
14923
- {
14924
- if ($arguments = $command->getArguments()) {
14925
- $length = count($arguments);
14926
-
14927
- for ($i = 0; $i < $length - 1; ++$i) {
14928
- $arguments[$i] = "$prefix{$arguments[$i]}";
14929
- }
14930
-
14931
- $command->setRawArguments($arguments);
14932
- }
14933
- }
14934
-
14935
- /**
14936
- * Applies the specified prefix to the keys of a SORT command.
14937
- *
14938
- * @param CommandInterface $command Command instance.
14939
- * @param string $prefix Prefix string.
14940
- */
14941
- public static function sort(CommandInterface $command, $prefix)
14942
- {
14943
- if ($arguments = $command->getArguments()) {
14944
- $arguments[0] = "$prefix{$arguments[0]}";
14945
-
14946
- if (($count = count($arguments)) > 1) {
14947
- for ($i = 1; $i < $count; ++$i) {
14948
- switch (strtoupper($arguments[$i])) {
14949
- case 'BY':
14950
- case 'STORE':
14951
- $arguments[$i] = "$prefix{$arguments[++$i]}";
14952
- break;
14953
-
14954
- case 'GET':
14955
- $value = $arguments[++$i];
14956
- if ($value !== '#') {
14957
- $arguments[$i] = "$prefix$value";
14958
- }
14959
- break;
14960
-
14961
- case 'LIMIT';
14962
- $i += 2;
14963
- break;
14964
- }
14965
- }
14966
- }
14967
-
14968
- $command->setRawArguments($arguments);
14969
- }
14970
- }
14971
-
14972
- /**
14973
- * Applies the specified prefix to the keys of an EVAL-based command.
14974
- *
14975
- * @param CommandInterface $command Command instance.
14976
- * @param string $prefix Prefix string.
14977
- */
14978
- public static function evalKeys(CommandInterface $command, $prefix)
14979
- {
14980
- if ($arguments = $command->getArguments()) {
14981
- for ($i = 2; $i < $arguments[1] + 2; ++$i) {
14982
- $arguments[$i] = "$prefix{$arguments[$i]}";
14983
- }
14984
-
14985
- $command->setRawArguments($arguments);
14986
- }
14987
- }
14988
-
14989
- /**
14990
- * Applies the specified prefix to the keys of Z[INTERSECTION|UNION]STORE.
14991
- *
14992
- * @param CommandInterface $command Command instance.
14993
- * @param string $prefix Prefix string.
14994
- */
14995
- public static function zsetStore(CommandInterface $command, $prefix)
14996
- {
14997
- if ($arguments = $command->getArguments()) {
14998
- $arguments[0] = "$prefix{$arguments[0]}";
14999
- $length = ((int) $arguments[1]) + 2;
15000
-
15001
- for ($i = 2; $i < $length; ++$i) {
15002
- $arguments[$i] = "$prefix{$arguments[$i]}";
15003
- }
15004
-
15005
- $command->setRawArguments($arguments);
15006
- }
15007
- }
15008
-
15009
- /**
15010
- * Applies the specified prefix to the key of a MIGRATE command.
15011
- *
15012
- * @param CommandInterface $command Command instance.
15013
- * @param string $prefix Prefix string.
15014
- */
15015
- public static function migrate(CommandInterface $command, $prefix)
15016
- {
15017
- if ($arguments = $command->getArguments()) {
15018
- $arguments[2] = "$prefix{$arguments[2]}";
15019
- $command->setRawArguments($arguments);
15020
- }
15021
- }
15022
-
15023
- /**
15024
- * Applies the specified prefix to the key of a GEORADIUS command.
15025
- *
15026
- * @param CommandInterface $command Command instance.
15027
- * @param string $prefix Prefix string.
15028
- */
15029
- public static function georadius(CommandInterface $command, $prefix)
15030
- {
15031
- if ($arguments = $command->getArguments()) {
15032
- $arguments[0] = "$prefix{$arguments[0]}";
15033
- $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
15034
-
15035
- if (($count = count($arguments)) > $startIndex) {
15036
- for ($i = $startIndex; $i < $count; ++$i) {
15037
- switch (strtoupper($arguments[$i])) {
15038
- case 'STORE':
15039
- case 'STOREDIST':
15040
- $arguments[$i] = "$prefix{$arguments[++$i]}";
15041
- break;
15042
-
15043
- }
15044
- }
15045
- }
15046
-
15047
- $command->setRawArguments($arguments);
15048
- }
15049
- }
15050
- }
15051
-
15052
- /**
15053
- * Default implementation of a command processors chain.
15054
- *
15055
- * @author Daniele Alessandri <suppakilla@gmail.com>
15056
- */
15057
- class ProcessorChain implements \ArrayAccess, ProcessorInterface
15058
- {
15059
- private $processors = array();
15060
-
15061
- /**
15062
- * @param array $processors List of instances of ProcessorInterface.
15063
- */
15064
- public function __construct($processors = array())
15065
- {
15066
- foreach ($processors as $processor) {
15067
- $this->add($processor);
15068
- }
15069
- }
15070
-
15071
- /**
15072
- * {@inheritdoc}
15073
- */
15074
- public function add(ProcessorInterface $processor)
15075
- {
15076
- $this->processors[] = $processor;
15077
- }
15078
-
15079
- /**
15080
- * {@inheritdoc}
15081
- */
15082
- public function remove(ProcessorInterface $processor)
15083
- {
15084
- if (false !== $index = array_search($processor, $this->processors, true)) {
15085
- unset($this[$index]);
15086
- }
15087
- }
15088
-
15089
- /**
15090
- * {@inheritdoc}
15091
- */
15092
- public function process(CommandInterface $command)
15093
- {
15094
- for ($i = 0; $i < $count = count($this->processors); ++$i) {
15095
- $this->processors[$i]->process($command);
15096
- }
15097
- }
15098
-
15099
- /**
15100
- * {@inheritdoc}
15101
- */
15102
- public function getProcessors()
15103
- {
15104
- return $this->processors;
15105
- }
15106
-
15107
- /**
15108
- * Returns an iterator over the list of command processor in the chain.
15109
- *
15110
- * @return \ArrayIterator
15111
- */
15112
- public function getIterator()
15113
- {
15114
- return new \ArrayIterator($this->processors);
15115
- }
15116
-
15117
- /**
15118
- * Returns the number of command processors in the chain.
15119
- *
15120
- * @return int
15121
- */
15122
- public function count()
15123
- {
15124
- return count($this->processors);
15125
- }
15126
-
15127
- /**
15128
- * {@inheritdoc}
15129
- */
15130
- public function offsetExists($index)
15131
- {
15132
- return isset($this->processors[$index]);
15133
- }
15134
-
15135
- /**
15136
- * {@inheritdoc}
15137
- */
15138
- public function offsetGet($index)
15139
- {
15140
- return $this->processors[$index];
15141
- }
15142
-
15143
- /**
15144
- * {@inheritdoc}
15145
- */
15146
- public function offsetSet($index, $processor)
15147
- {
15148
- if (!$processor instanceof ProcessorInterface) {
15149
- throw new \InvalidArgumentException(
15150
- 'A processor chain accepts only instances of '.
15151
- "'Predis\Command\Processor\ProcessorInterface'."
15152
- );
15153
- }
15154
-
15155
- $this->processors[$index] = $processor;
15156
- }
15157
-
15158
- /**
15159
- * {@inheritdoc}
15160
- */
15161
- public function offsetUnset($index)
15162
- {
15163
- unset($this->processors[$index]);
15164
- $this->processors = array_values($this->processors);
15165
- }
15166
- }
15167
-
15168
- /* --------------------------------------------------------------------------- */
15169
-
15170
- namespace Predis\Cluster\Hash;
15171
-
15172
- /**
15173
- * An hash generator implements the logic used to calculate the hash of a key to
15174
- * distribute operations among Redis nodes.
15175
- *
15176
- * @author Daniele Alessandri <suppakilla@gmail.com>
15177
- */
15178
- interface HashGeneratorInterface
15179
- {
15180
- /**
15181
- * Generates an hash from a string to be used for distribution.
15182
- *
15183
- * @param string $value String value.
15184
- *
15185
- * @return int
15186
- */
15187
- public function hash($value);
15188
- }
15189
-
15190
- /**
15191
- * Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
15192
- *
15193
- * @author Daniele Alessandri <suppakilla@gmail.com>
15194
- */
15195
- class CRC16 implements HashGeneratorInterface
15196
- {
15197
- private static $CCITT_16 = array(
15198
- 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
15199
- 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
15200
- 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
15201
- 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
15202
- 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
15203
- 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
15204
- 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
15205
- 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
15206
- 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
15207
- 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
15208
- 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
15209
- 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
15210
- 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
15211
- 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
15212
- 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
15213
- 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
15214
- 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
15215
- 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
15216
- 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
15217
- 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
15218
- 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
15219
- 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
15220
- 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
15221
- 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
15222
- 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
15223
- 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
15224
- 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
15225
- 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
15226
- 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
15227
- 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
15228
- 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
15229
- 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
15230
- );
15231
-
15232
- /**
15233
- * {@inheritdoc}
15234
- */
15235
- public function hash($value)
15236
- {
15237
- // CRC-CCITT-16 algorithm
15238
- $crc = 0;
15239
- $CCITT_16 = self::$CCITT_16;
15240
-
15241
- $value = (string) $value;
15242
- $strlen = strlen($value);
15243
-
15244
- for ($i = 0; $i < $strlen; ++$i) {
15245
- $crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
15246
- }
15247
-
15248
- return $crc;
15249
- }
15250
- }
15251
-
15252
- /* --------------------------------------------------------------------------- */
15253
-
15254
- namespace Predis\Protocol\Text;
15255
-
15256
- use Predis\Command\CommandInterface;
15257
- use Predis\Connection\CompositeConnectionInterface;
15258
- use Predis\Protocol\ProtocolProcessorInterface;
15259
- use Predis\Protocol\RequestSerializerInterface;
15260
- use Predis\Protocol\ResponseReaderInterface;
15261
- use Predis\CommunicationException;
15262
- use Predis\Protocol\ProtocolException;
15263
- use Predis\Response\Error as ErrorResponse;
15264
- use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;
15265
- use Predis\Response\Status as StatusResponse;
15266
-
15267
- /**
15268
- * Composite protocol processor for the standard Redis wire protocol using
15269
- * pluggable handlers to serialize requests and deserialize responses.
15270
- *
15271
- * @link http://redis.io/topics/protocol
15272
- *
15273
- * @author Daniele Alessandri <suppakilla@gmail.com>
15274
- */
15275
- class CompositeProtocolProcessor implements ProtocolProcessorInterface
15276
- {
15277
- /*
15278
- * @var RequestSerializerInterface
15279
- */
15280
- protected $serializer;
15281
-
15282
- /*
15283
- * @var ResponseReaderInterface
15284
- */
15285
- protected $reader;
15286
-
15287
- /**
15288
- * @param RequestSerializerInterface $serializer Request serializer.
15289
- * @param ResponseReaderInterface $reader Response reader.
15290
- */
15291
- public function __construct(
15292
- RequestSerializerInterface $serializer = null,
15293
- ResponseReaderInterface $reader = null
15294
- ) {
15295
- $this->setRequestSerializer($serializer ?: new RequestSerializer());
15296
- $this->setResponseReader($reader ?: new ResponseReader());
15297
- }
15298
-
15299
- /**
15300
- * {@inheritdoc}
15301
- */
15302
- public function write(CompositeConnectionInterface $connection, CommandInterface $command)
15303
- {
15304
- $connection->writeBuffer($this->serializer->serialize($command));
15305
- }
15306
-
15307
- /**
15308
- * {@inheritdoc}
15309
- */
15310
- public function read(CompositeConnectionInterface $connection)
15311
- {
15312
- return $this->reader->read($connection);
15313
- }
15314
-
15315
- /**
15316
- * Sets the request serializer used by the protocol processor.
15317
- *
15318
- * @param RequestSerializerInterface $serializer Request serializer.
15319
- */
15320
- public function setRequestSerializer(RequestSerializerInterface $serializer)
15321
- {
15322
- $this->serializer = $serializer;
15323
- }
15324
-
15325
- /**
15326
- * Returns the request serializer used by the protocol processor.
15327
- *
15328
- * @return RequestSerializerInterface
15329
- */
15330
- public function getRequestSerializer()
15331
- {
15332
- return $this->serializer;
15333
- }
15334
-
15335
- /**
15336
- * Sets the response reader used by the protocol processor.
15337
- *
15338
- * @param ResponseReaderInterface $reader Response reader.
15339
- */
15340
- public function setResponseReader(ResponseReaderInterface $reader)
15341
- {
15342
- $this->reader = $reader;
15343
- }
15344
-
15345
- /**
15346
- * Returns the Response reader used by the protocol processor.
15347
- *
15348
- * @return ResponseReaderInterface
15349
- */
15350
- public function getResponseReader()
15351
- {
15352
- return $this->reader;
15353
- }
15354
- }
15355
-
15356
- /**
15357
- * Request serializer for the standard Redis wire protocol.
15358
- *
15359
- * @link http://redis.io/topics/protocol
15360
- *
15361
- * @author Daniele Alessandri <suppakilla@gmail.com>
15362
- */
15363
- class RequestSerializer implements RequestSerializerInterface
15364
- {
15365
- /**
15366
- * {@inheritdoc}
15367
- */
15368
- public function serialize(CommandInterface $command)
15369
- {
15370
- $commandID = $command->getId();
15371
- $arguments = $command->getArguments();
15372
-
15373
- $cmdlen = strlen($commandID);
15374
- $reqlen = count($arguments) + 1;
15375
-
15376
- $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
15377
-
15378
- foreach ($arguments as $argument) {
15379
- $arglen = strlen($argument);
15380
- $buffer .= "\${$arglen}\r\n{$argument}\r\n";
15381
- }
15382
-
15383
- return $buffer;
15384
- }
15385
- }
15386
-
15387
- /**
15388
- * Response reader for the standard Redis wire protocol.
15389
- *
15390
- * @link http://redis.io/topics/protocol
15391
- *
15392
- * @author Daniele Alessandri <suppakilla@gmail.com>
15393
- */
15394
- class ResponseReader implements ResponseReaderInterface
15395
- {
15396
- protected $handlers;
15397
-
15398
- /**
15399
- *
15400
- */
15401
- public function __construct()
15402
- {
15403
- $this->handlers = $this->getDefaultHandlers();
15404
- }
15405
-
15406
- /**
15407
- * Returns the default handlers for the supported type of responses.
15408
- *
15409
- * @return array
15410
- */
15411
- protected function getDefaultHandlers()
15412
- {
15413
- return array(
15414
- '+' => new Handler\StatusResponse(),
15415
- '-' => new Handler\ErrorResponse(),
15416
- ':' => new Handler\IntegerResponse(),
15417
- '$' => new Handler\BulkResponse(),
15418
- '*' => new Handler\MultiBulkResponse(),
15419
- );
15420
- }
15421
-
15422
- /**
15423
- * Sets the handler for the specified prefix identifying the response type.
15424
- *
15425
- * @param string $prefix Identifier of the type of response.
15426
- * @param Handler\ResponseHandlerInterface $handler Response handler.
15427
- */
15428
- public function setHandler($prefix, Handler\ResponseHandlerInterface $handler)
15429
- {
15430
- $this->handlers[$prefix] = $handler;
15431
- }
15432
-
15433
- /**
15434
- * Returns the response handler associated to a certain type of response.
15435
- *
15436
- * @param string $prefix Identifier of the type of response.
15437
- *
15438
- * @return Handler\ResponseHandlerInterface
15439
- */
15440
- public function getHandler($prefix)
15441
- {
15442
- if (isset($this->handlers[$prefix])) {
15443
- return $this->handlers[$prefix];
15444
- }
15445
-
15446
- return;
15447
- }
15448
-
15449
- /**
15450
- * {@inheritdoc}
15451
- */
15452
- public function read(CompositeConnectionInterface $connection)
15453
- {
15454
- $header = $connection->readLine();
15455
-
15456
- if ($header === '') {
15457
- $this->onProtocolError($connection, 'Unexpected empty reponse header.');
15458
- }
15459
-
15460
- $prefix = $header[0];
15461
-
15462
- if (!isset($this->handlers[$prefix])) {
15463
- $this->onProtocolError($connection, "Unknown response prefix: '$prefix'.");
15464
- }
15465
-
15466
- $payload = $this->handlers[$prefix]->handle($connection, substr($header, 1));
15467
-
15468
- return $payload;
15469
- }
15470
-
15471
- /**
15472
- * Handles protocol errors generated while reading responses from a
15473
- * connection.
15474
- *
15475
- * @param CompositeConnectionInterface $connection Redis connection that generated the error.
15476
- * @param string $message Error message.
15477
- */
15478
- protected function onProtocolError(CompositeConnectionInterface $connection, $message)
15479
- {
15480
- CommunicationException::handle(
15481
- new ProtocolException($connection, $message)
15482
- );
15483
- }
15484
- }
15485
-
15486
- /**
15487
- * Protocol processor for the standard Redis wire protocol.
15488
- *
15489
- * @link http://redis.io/topics/protocol
15490
- *
15491
- * @author Daniele Alessandri <suppakilla@gmail.com>
15492
- */
15493
- class ProtocolProcessor implements ProtocolProcessorInterface
15494
- {
15495
- protected $mbiterable;
15496
- protected $serializer;
15497
-
15498
- /**
15499
- *
15500
- */
15501
- public function __construct()
15502
- {
15503
- $this->mbiterable = false;
15504
- $this->serializer = new RequestSerializer();
15505
- }
15506
-
15507
- /**
15508
- * {@inheritdoc}
15509
- */
15510
- public function write(CompositeConnectionInterface $connection, CommandInterface $command)
15511
- {
15512
- $request = $this->serializer->serialize($command);
15513
- $connection->writeBuffer($request);
15514
- }
15515
-
15516
- /**
15517
- * {@inheritdoc}
15518
- */
15519
- public function read(CompositeConnectionInterface $connection)
15520
- {
15521
- $chunk = $connection->readLine();
15522
- $prefix = $chunk[0];
15523
- $payload = substr($chunk, 1);
15524
-
15525
- switch ($prefix) {
15526
- case '+':
15527
- return new StatusResponse($payload);
15528
-
15529
- case '$':
15530
- $size = (int) $payload;
15531
- if ($size === -1) {
15532
- return;
15533
- }
15534
-
15535
- return substr($connection->readBuffer($size + 2), 0, -2);
15536
-
15537
- case '*':
15538
- $count = (int) $payload;
15539
-
15540
- if ($count === -1) {
15541
- return;
15542
- }
15543
- if ($this->mbiterable) {
15544
- return new MultiBulkIterator($connection, $count);
15545
- }
15546
-
15547
- $multibulk = array();
15548
-
15549
- for ($i = 0; $i < $count; ++$i) {
15550
- $multibulk[$i] = $this->read($connection);
15551
- }
15552
-
15553
- return $multibulk;
15554
-
15555
- case ':':
15556
- $integer = (int) $payload;
15557
- return $integer == $payload ? $integer : $payload;
15558
-
15559
- case '-':
15560
- return new ErrorResponse($payload);
15561
-
15562
- default:
15563
- CommunicationException::handle(new ProtocolException(
15564
- $connection, "Unknown response prefix: '$prefix'."
15565
- ));
15566
-
15567
- return;
15568
- }
15569
- }
15570
-
15571
- /**
15572
- * Enables or disables returning multibulk responses as specialized PHP
15573
- * iterators used to stream bulk elements of a multibulk response instead
15574
- * returning a plain array.
15575
- *
15576
- * Streamable multibulk responses are not globally supported by the
15577
- * abstractions built-in into Predis, such as transactions or pipelines.
15578
- * Use them with care!
15579
- *
15580
- * @param bool $value Enable or disable streamable multibulk responses.
15581
- */
15582
- public function useIterableMultibulk($value)
15583
- {
15584
- $this->mbiterable = (bool) $value;
15585
- }
15586
- }
15587
-
15588
- /* --------------------------------------------------------------------------- */
15589
-
15590
- namespace Predis\PubSub;
15591
-
15592
- use Predis\ClientException;
15593
- use Predis\ClientInterface;
15594
- use Predis\Command\Command;
15595
- use Predis\Connection\AggregateConnectionInterface;
15596
- use Predis\NotSupportedException;
15597
-
15598
- /**
15599
- * Base implementation of a PUB/SUB consumer abstraction based on PHP iterators.
15600
- *
15601
- * @author Daniele Alessandri <suppakilla@gmail.com>
15602
- */
15603
- abstract class AbstractConsumer implements \Iterator
15604
- {
15605
- const SUBSCRIBE = 'subscribe';
15606
- const UNSUBSCRIBE = 'unsubscribe';
15607
- const PSUBSCRIBE = 'psubscribe';
15608
- const PUNSUBSCRIBE = 'punsubscribe';
15609
- const MESSAGE = 'message';
15610
- const PMESSAGE = 'pmessage';
15611
- const PONG = 'pong';
15612
-
15613
- const STATUS_VALID = 1; // 0b0001
15614
- const STATUS_SUBSCRIBED = 2; // 0b0010
15615
- const STATUS_PSUBSCRIBED = 4; // 0b0100
15616
-
15617
- private $position = null;
15618
- private $statusFlags = self::STATUS_VALID;
15619
-
15620
- /**
15621
- * Automatically stops the consumer when the garbage collector kicks in.
15622
- */
15623
- public function __destruct()
15624
- {
15625
- $this->stop(true);
15626
- }
15627
-
15628
- /**
15629
- * Checks if the specified flag is valid based on the state of the consumer.
15630
- *
15631
- * @param int $value Flag.
15632
- *
15633
- * @return bool
15634
- */
15635
- protected function isFlagSet($value)
15636
- {
15637
- return ($this->statusFlags & $value) === $value;
15638
- }
15639
-
15640
- /**
15641
- * Subscribes to the specified channels.
15642
- *
15643
- * @param mixed $channel,... One or more channel names.
15644
- */
15645
- public function subscribe($channel /*, ... */)
15646
- {
15647
- $this->writeRequest(self::SUBSCRIBE, func_get_args());
15648
- $this->statusFlags |= self::STATUS_SUBSCRIBED;
15649
- }
15650
-
15651
- /**
15652
- * Unsubscribes from the specified channels.
15653
- *
15654
- * @param string ... One or more channel names.
15655
- */
15656
- public function unsubscribe(/* ... */)
15657
- {
15658
- $this->writeRequest(self::UNSUBSCRIBE, func_get_args());
15659
- }
15660
-
15661
- /**
15662
- * Subscribes to the specified channels using a pattern.
15663
- *
15664
- * @param mixed $pattern,... One or more channel name patterns.
15665
- */
15666
- public function psubscribe($pattern /* ... */)
15667
- {
15668
- $this->writeRequest(self::PSUBSCRIBE, func_get_args());
15669
- $this->statusFlags |= self::STATUS_PSUBSCRIBED;
15670
- }
15671
-
15672
- /**
15673
- * Unsubscribes from the specified channels using a pattern.
15674
- *
15675
- * @param string ... One or more channel name patterns.
15676
- */
15677
- public function punsubscribe(/* ... */)
15678
- {
15679
- $this->writeRequest(self::PUNSUBSCRIBE, func_get_args());
15680
- }
15681
-
15682
- /**
15683
- * PING the server with an optional payload that will be echoed as a
15684
- * PONG message in the pub/sub loop.
15685
- *
15686
- * @param string $payload Optional PING payload.
15687
- */
15688
- public function ping($payload = null)
15689
- {
15690
- $this->writeRequest('PING', array($payload));
15691
- }
15692
-
15693
- /**
15694
- * Closes the context by unsubscribing from all the subscribed channels. The
15695
- * context can be forcefully closed by dropping the underlying connection.
15696
- *
15697
- * @param bool $drop Indicates if the context should be closed by dropping the connection.
15698
- *
15699
- * @return bool Returns false when there are no pending messages.
15700
- */
15701
- public function stop($drop = false)
15702
- {
15703
- if (!$this->valid()) {
15704
- return false;
15705
- }
15706
-
15707
- if ($drop) {
15708
- $this->invalidate();
15709
- $this->disconnect();
15710
- } else {
15711
- if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) {
15712
- $this->unsubscribe();
15713
- }
15714
- if ($this->isFlagSet(self::STATUS_PSUBSCRIBED)) {
15715
- $this->punsubscribe();
15716
- }
15717
- }
15718
-
15719
- return !$drop;
15720
- }
15721
-
15722
- /**
15723
- * Closes the underlying connection when forcing a disconnection.
15724
- */
15725
- abstract protected function disconnect();
15726
-
15727
- /**
15728
- * Writes a Redis command on the underlying connection.
15729
- *
15730
- * @param string $method Command ID.
15731
- * @param array $arguments Arguments for the command.
15732
- */
15733
- abstract protected function writeRequest($method, $arguments);
15734
-
15735
- /**
15736
- * {@inheritdoc}
15737
- */
15738
- public function rewind()
15739
- {
15740
- // NOOP
15741
- }
15742
-
15743
- /**
15744
- * Returns the last message payload retrieved from the server and generated
15745
- * by one of the active subscriptions.
15746
- *
15747
- * @return array
15748
- */
15749
- public function current()
15750
- {
15751
- return $this->getValue();
15752
- }
15753
-
15754
- /**
15755
- * {@inheritdoc}
15756
- */
15757
- public function key()
15758
- {
15759
- return $this->position;
15760
- }
15761
-
15762
- /**
15763
- * {@inheritdoc}
15764
- */
15765
- public function next()
15766
- {
15767
- if ($this->valid()) {
15768
- ++$this->position;
15769
- }
15770
-
15771
- return $this->position;
15772
- }
15773
-
15774
- /**
15775
- * Checks if the the consumer is still in a valid state to continue.
15776
- *
15777
- * @return bool
15778
- */
15779
- public function valid()
15780
- {
15781
- $isValid = $this->isFlagSet(self::STATUS_VALID);
15782
- $subscriptionFlags = self::STATUS_SUBSCRIBED | self::STATUS_PSUBSCRIBED;
15783
- $hasSubscriptions = ($this->statusFlags & $subscriptionFlags) > 0;
15784
-
15785
- return $isValid && $hasSubscriptions;
15786
- }
15787
-
15788
- /**
15789
- * Resets the state of the consumer.
15790
- */
15791
- protected function invalidate()
15792
- {
15793
- $this->statusFlags = 0; // 0b0000;
15794
- }
15795
-
15796
- /**
15797
- * Waits for a new message from the server generated by one of the active
15798
- * subscriptions and returns it when available.
15799
- *
15800
- * @return array
15801
- */
15802
- abstract protected function getValue();
15803
- }
15804
-
15805
- /**
15806
- * PUB/SUB consumer abstraction.
15807
- *
15808
- * @author Daniele Alessandri <suppakilla@gmail.com>
15809
- */
15810
- class Consumer extends AbstractConsumer
15811
- {
15812
- private $client;
15813
- private $options;
15814
-
15815
- /**
15816
- * @param ClientInterface $client Client instance used by the consumer.
15817
- * @param array $options Options for the consumer initialization.
15818
- */
15819
- public function __construct(ClientInterface $client, array $options = null)
15820
- {
15821
- $this->checkCapabilities($client);
15822
-
15823
- $this->options = $options ?: array();
15824
- $this->client = $client;
15825
-
15826
- $this->genericSubscribeInit('subscribe');
15827
- $this->genericSubscribeInit('psubscribe');
15828
- }
15829
-
15830
- /**
15831
- * Returns the underlying client instance used by the pub/sub iterator.
15832
- *
15833
- * @return ClientInterface
15834
- */
15835
- public function getClient()
15836
- {
15837
- return $this->client;
15838
- }
15839
-
15840
- /**
15841
- * Checks if the client instance satisfies the required conditions needed to
15842
- * initialize a PUB/SUB consumer.
15843
- *
15844
- * @param ClientInterface $client Client instance used by the consumer.
15845
- *
15846
- * @throws NotSupportedException
15847
- */
15848
- private function checkCapabilities(ClientInterface $client)
15849
- {
15850
- if ($client->getConnection() instanceof AggregateConnectionInterface) {
15851
- throw new NotSupportedException(
15852
- 'Cannot initialize a PUB/SUB consumer over aggregate connections.'
15853
- );
15854
- }
15855
-
15856
- $commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe');
15857
-
15858
- if ($client->getProfile()->supportsCommands($commands) === false) {
15859
- throw new NotSupportedException(
15860
- 'The current profile does not support PUB/SUB related commands.'
15861
- );
15862
- }
15863
- }
15864
-
15865
- /**
15866
- * This method shares the logic to handle both SUBSCRIBE and PSUBSCRIBE.
15867
- *
15868
- * @param string $subscribeAction Type of subscription.
15869
- */
15870
- private function genericSubscribeInit($subscribeAction)
15871
- {
15872
- if (isset($this->options[$subscribeAction])) {
15873
- $this->$subscribeAction($this->options[$subscribeAction]);
15874
- }
15875
- }
15876
-
15877
- /**
15878
- * {@inheritdoc}
15879
- */
15880
- protected function writeRequest($method, $arguments)
15881
- {
15882
- $this->client->getConnection()->writeRequest(
15883
- $this->client->createCommand($method,
15884
- Command::normalizeArguments($arguments)
15885
- )
15886
- );
15887
- }
15888
-
15889
- /**
15890
- * {@inheritdoc}
15891
- */
15892
- protected function disconnect()
15893
- {
15894
- $this->client->disconnect();
15895
- }
15896
-
15897
- /**
15898
- * {@inheritdoc}
15899
- */
15900
- protected function getValue()
15901
- {
15902
- $response = $this->client->getConnection()->read();
15903
-
15904
- switch ($response[0]) {
15905
- case self::SUBSCRIBE:
15906
- case self::UNSUBSCRIBE:
15907
- case self::PSUBSCRIBE:
15908
- case self::PUNSUBSCRIBE:
15909
- if ($response[2] === 0) {
15910
- $this->invalidate();
15911
- }
15912
- // The missing break here is intentional as we must process
15913
- // subscriptions and unsubscriptions as standard messages.
15914
- // no break
15915
-
15916
- case self::MESSAGE:
15917
- return (object) array(
15918
- 'kind' => $response[0],
15919
- 'channel' => $response[1],
15920
- 'payload' => $response[2],
15921
- );
15922
-
15923
- case self::PMESSAGE:
15924
- return (object) array(
15925
- 'kind' => $response[0],
15926
- 'pattern' => $response[1],
15927
- 'channel' => $response[2],
15928
- 'payload' => $response[3],
15929
- );
15930
-
15931
- case self::PONG:
15932
- return (object) array(
15933
- 'kind' => $response[0],
15934
- 'payload' => $response[1],
15935
- );
15936
-
15937
- default:
15938
- throw new ClientException(
15939
- "Unknown message type '{$response[0]}' received in the PUB/SUB context."
15940
- );
15941
- }
15942
- }
15943
- }
15944
-
15945
- /**
15946
- * Method-dispatcher loop built around the client-side abstraction of a Redis
15947
- * PUB / SUB context.
15948
- *
15949
- * @author Daniele Alessandri <suppakilla@gmail.com>
15950
- */
15951
- class DispatcherLoop
15952
- {
15953
- private $pubsub;
15954
-
15955
- protected $callbacks;
15956
- protected $defaultCallback;
15957
- protected $subscriptionCallback;
15958
-
15959
- /**
15960
- * @param Consumer $pubsub PubSub consumer instance used by the loop.
15961
- */
15962
- public function __construct(Consumer $pubsub)
15963
- {
15964
- $this->callbacks = array();
15965
- $this->pubsub = $pubsub;
15966
- }
15967
-
15968
- /**
15969
- * Checks if the passed argument is a valid callback.
15970
- *
15971
- * @param mixed $callable A callback.
15972
- *
15973
- * @throws \InvalidArgumentException
15974
- */
15975
- protected function assertCallback($callable)
15976
- {
15977
- if (!is_callable($callable)) {
15978
- throw new \InvalidArgumentException('The given argument must be a callable object.');
15979
- }
15980
- }
15981
-
15982
- /**
15983
- * Returns the underlying PUB / SUB context.
15984
- *
15985
- * @return Consumer
15986
- */
15987
- public function getPubSubConsumer()
15988
- {
15989
- return $this->pubsub;
15990
- }
15991
-
15992
- /**
15993
- * Sets a callback that gets invoked upon new subscriptions.
15994
- *
15995
- * @param mixed $callable A callback.
15996
- */
15997
- public function subscriptionCallback($callable = null)
15998
- {
15999
- if (isset($callable)) {
16000
- $this->assertCallback($callable);
16001
- }
16002
-
16003
- $this->subscriptionCallback = $callable;
16004
- }
16005
-
16006
- /**
16007
- * Sets a callback that gets invoked when a message is received on a
16008
- * channel that does not have an associated callback.
16009
- *
16010
- * @param mixed $callable A callback.
16011
- */
16012
- public function defaultCallback($callable = null)
16013
- {
16014
- if (isset($callable)) {
16015
- $this->assertCallback($callable);
16016
- }
16017
-
16018
- $this->subscriptionCallback = $callable;
16019
- }
16020
-
16021
- /**
16022
- * Binds a callback to a channel.
16023
- *
16024
- * @param string $channel Channel name.
16025
- * @param callable $callback A callback.
16026
- */
16027
- public function attachCallback($channel, $callback)
16028
- {
16029
- $callbackName = $this->getPrefixKeys().$channel;
16030
-
16031
- $this->assertCallback($callback);
16032
- $this->callbacks[$callbackName] = $callback;
16033
- $this->pubsub->subscribe($channel);
16034
- }
16035
-
16036
- /**
16037
- * Stops listening to a channel and removes the associated callback.
16038
- *
16039
- * @param string $channel Redis channel.
16040
- */
16041
- public function detachCallback($channel)
16042
- {
16043
- $callbackName = $this->getPrefixKeys().$channel;
16044
-
16045
- if (isset($this->callbacks[$callbackName])) {
16046
- unset($this->callbacks[$callbackName]);
16047
- $this->pubsub->unsubscribe($channel);
16048
- }
16049
- }
16050
-
16051
- /**
16052
- * Starts the dispatcher loop.
16053
- */
16054
- public function run()
16055
- {
16056
- foreach ($this->pubsub as $message) {
16057
- $kind = $message->kind;
16058
-
16059
- if ($kind !== Consumer::MESSAGE && $kind !== Consumer::PMESSAGE) {
16060
- if (isset($this->subscriptionCallback)) {
16061
- $callback = $this->subscriptionCallback;
16062
- call_user_func($callback, $message);
16063
- }
16064
-
16065
- continue;
16066
- }
16067
-
16068
- if (isset($this->callbacks[$message->channel])) {
16069
- $callback = $this->callbacks[$message->channel];
16070
- call_user_func($callback, $message->payload);
16071
- } elseif (isset($this->defaultCallback)) {
16072
- $callback = $this->defaultCallback;
16073
- call_user_func($callback, $message);
16074
- }
16075
- }
16076
- }
16077
-
16078
- /**
16079
- * Terminates the dispatcher loop.
16080
- */
16081
- public function stop()
16082
- {
16083
- $this->pubsub->stop();
16084
- }
16085
-
16086
- /**
16087
- * Return the prefix used for keys.
16088
- *
16089
- * @return string
16090
- */
16091
- protected function getPrefixKeys()
16092
- {
16093
- $options = $this->pubsub->getClient()->getOptions();
16094
-
16095
- if (isset($options->prefix)) {
16096
- return $options->prefix->getPrefix();
16097
- }
16098
-
16099
- return '';
16100
- }
16101
- }
16102
-
16103
- /* --------------------------------------------------------------------------- */
16104
-
16105
- namespace Predis\Transaction;
16106
-
16107
- use Predis\PredisException;
16108
- use Predis\ClientContextInterface;
16109
- use Predis\ClientException;
16110
- use Predis\ClientInterface;
16111
- use Predis\Command\CommandInterface;
16112
- use Predis\CommunicationException;
16113
- use Predis\Connection\AggregateConnectionInterface;
16114
- use Predis\NotSupportedException;
16115
- use Predis\Protocol\ProtocolException;
16116
- use Predis\Response\ErrorInterface as ErrorResponseInterface;
16117
- use Predis\Response\ServerException;
16118
- use Predis\Response\Status as StatusResponse;
16119
-
16120
- /**
16121
- * Exception class that identifies a MULTI / EXEC transaction aborted by Redis.
16122
- *
16123
- * @author Daniele Alessandri <suppakilla@gmail.com>
16124
- */
16125
- class AbortedMultiExecException extends PredisException
16126
- {
16127
- private $transaction;
16128
-
16129
- /**
16130
- * @param MultiExec $transaction Transaction that generated the exception.
16131
- * @param string $message Error message.
16132
- * @param int $code Error code.
16133
- */
16134
- public function __construct(MultiExec $transaction, $message, $code = null)
16135
- {
16136
- parent::__construct($message, $code);
16137
- $this->transaction = $transaction;
16138
- }
16139
-
16140
- /**
16141
- * Returns the transaction that generated the exception.
16142
- *
16143
- * @return MultiExec
16144
- */
16145
- public function getTransaction()
16146
- {
16147
- return $this->transaction;
16148
- }
16149
- }
16150
-
16151
- /**
16152
- * Utility class used to track the state of a MULTI / EXEC transaction.
16153
- *
16154
- * @author Daniele Alessandri <suppakilla@gmail.com>
16155
- */
16156
- class MultiExecState
16157
- {
16158
- const INITIALIZED = 1; // 0b00001
16159
- const INSIDEBLOCK = 2; // 0b00010
16160
- const DISCARDED = 4; // 0b00100
16161
- const CAS = 8; // 0b01000
16162
- const WATCH = 16; // 0b10000
16163
-
16164
- private $flags;
16165
-
16166
- /**
16167
- *
16168
- */
16169
- public function __construct()
16170
- {
16171
- $this->flags = 0;
16172
- }
16173
-
16174
- /**
16175
- * Sets the internal state flags.
16176
- *
16177
- * @param int $flags Set of flags
16178
- */
16179
- public function set($flags)
16180
- {
16181
- $this->flags = $flags;
16182
- }
16183
-
16184
- /**
16185
- * Gets the internal state flags.
16186
- *
16187
- * @return int
16188
- */
16189
- public function get()
16190
- {
16191
- return $this->flags;
16192
- }
16193
-
16194
- /**
16195
- * Sets one or more flags.
16196
- *
16197
- * @param int $flags Set of flags
16198
- */
16199
- public function flag($flags)
16200
- {
16201
- $this->flags |= $flags;
16202
- }
16203
-
16204
- /**
16205
- * Resets one or more flags.
16206
- *
16207
- * @param int $flags Set of flags
16208
- */
16209
- public function unflag($flags)
16210
- {
16211
- $this->flags &= ~$flags;
16212
- }
16213
-
16214
- /**
16215
- * Returns if the specified flag or set of flags is set.
16216
- *
16217
- * @param int $flags Flag
16218
- *
16219
- * @return bool
16220
- */
16221
- public function check($flags)
16222
- {
16223
- return ($this->flags & $flags) === $flags;
16224
- }
16225
-
16226
- /**
16227
- * Resets the state of a transaction.
16228
- */
16229
- public function reset()
16230
- {
16231
- $this->flags = 0;
16232
- }
16233
-
16234
- /**
16235
- * Returns the state of the RESET flag.
16236
- *
16237
- * @return bool
16238
- */
16239
- public function isReset()
16240
- {
16241
- return $this->flags === 0;
16242
- }
16243
-
16244
- /**
16245
- * Returns the state of the INITIALIZED flag.
16246
- *
16247
- * @return bool
16248
- */
16249
- public function isInitialized()
16250
- {
16251
- return $this->check(self::INITIALIZED);
16252
- }
16253
-
16254
- /**
16255
- * Returns the state of the INSIDEBLOCK flag.
16256
- *
16257
- * @return bool
16258
- */
16259
- public function isExecuting()
16260
- {
16261
- return $this->check(self::INSIDEBLOCK);
16262
- }
16263
-
16264
- /**
16265
- * Returns the state of the CAS flag.
16266
- *
16267
- * @return bool
16268
- */
16269
- public function isCAS()
16270
- {
16271
- return $this->check(self::CAS);
16272
- }
16273
-
16274
- /**
16275
- * Returns if WATCH is allowed in the current state.
16276
- *
16277
- * @return bool
16278
- */
16279
- public function isWatchAllowed()
16280
- {
16281
- return $this->check(self::INITIALIZED) && !$this->check(self::CAS);
16282
- }
16283
-
16284
- /**
16285
- * Returns the state of the WATCH flag.
16286
- *
16287
- * @return bool
16288
- */
16289
- public function isWatching()
16290
- {
16291
- return $this->check(self::WATCH);
16292
- }
16293
-
16294
- /**
16295
- * Returns the state of the DISCARDED flag.
16296
- *
16297
- * @return bool
16298
- */
16299
- public function isDiscarded()
16300
- {
16301
- return $this->check(self::DISCARDED);
16302
- }
16303
- }
16304
-
16305
- /**
16306
- * Client-side abstraction of a Redis transaction based on MULTI / EXEC.
16307
- *
16308
- * {@inheritdoc}
16309
- *
16310
- * @author Daniele Alessandri <suppakilla@gmail.com>
16311
- */
16312
- class MultiExec implements ClientContextInterface
16313
- {
16314
- private $state;
16315
-
16316
- protected $client;
16317
- protected $commands;
16318
- protected $exceptions = true;
16319
- protected $attempts = 0;
16320
- protected $watchKeys = array();
16321
- protected $modeCAS = false;
16322
-
16323
- /**
16324
- * @param ClientInterface $client Client instance used by the transaction.
16325
- * @param array $options Initialization options.
16326
- */
16327
- public function __construct(ClientInterface $client, array $options = null)
16328
- {
16329
- $this->assertClient($client);
16330
-
16331
- $this->client = $client;
16332
- $this->state = new MultiExecState();
16333
-
16334
- $this->configure($client, $options ?: array());
16335
- $this->reset();
16336
- }
16337
-
16338
- /**
16339
- * Checks if the passed client instance satisfies the required conditions
16340
- * needed to initialize the transaction object.
16341
- *
16342
- * @param ClientInterface $client Client instance used by the transaction object.
16343
- *
16344
- * @throws NotSupportedException
16345
- */
16346
- private function assertClient(ClientInterface $client)
16347
- {
16348
- if ($client->getConnection() instanceof AggregateConnectionInterface) {
16349
- throw new NotSupportedException(
16350
- 'Cannot initialize a MULTI/EXEC transaction over aggregate connections.'
16351
- );
16352
- }
16353
-
16354
- if (!$client->getProfile()->supportsCommands(array('MULTI', 'EXEC', 'DISCARD'))) {
16355
- throw new NotSupportedException(
16356
- 'The current profile does not support MULTI, EXEC and DISCARD.'
16357
- );
16358
- }
16359
- }
16360
-
16361
- /**
16362
- * Configures the transaction using the provided options.
16363
- *
16364
- * @param ClientInterface $client Underlying client instance.
16365
- * @param array $options Array of options for the transaction.
16366
- **/
16367
- protected function configure(ClientInterface $client, array $options)
16368
- {
16369
- if (isset($options['exceptions'])) {
16370
- $this->exceptions = (bool) $options['exceptions'];
16371
- } else {
16372
- $this->exceptions = $client->getOptions()->exceptions;
16373
- }
16374
-
16375
- if (isset($options['cas'])) {
16376
- $this->modeCAS = (bool) $options['cas'];
16377
- }
16378
-
16379
- if (isset($options['watch']) && $keys = $options['watch']) {
16380
- $this->watchKeys = $keys;
16381
- }
16382
-
16383
- if (isset($options['retry'])) {
16384
- $this->attempts = (int) $options['retry'];
16385
- }
16386
- }
16387
-
16388
- /**
16389
- * Resets the state of the transaction.
16390
- */
16391
- protected function reset()
16392
- {
16393
- $this->state->reset();
16394
- $this->commands = new \SplQueue();
16395
- }
16396
-
16397
- /**
16398
- * Initializes the transaction context.
16399
- */
16400
- protected function initialize()
16401
- {
16402
- if ($this->state->isInitialized()) {
16403
- return;
16404
- }
16405
-
16406
- if ($this->modeCAS) {
16407
- $this->state->flag(MultiExecState::CAS);
16408
- }
16409
-
16410
- if ($this->watchKeys) {
16411
- $this->watch($this->watchKeys);
16412
- }
16413
-
16414
- $cas = $this->state->isCAS();
16415
- $discarded = $this->state->isDiscarded();
16416
-
16417
- if (!$cas || ($cas && $discarded)) {
16418
- $this->call('MULTI');
16419
-
16420
- if ($discarded) {
16421
- $this->state->unflag(MultiExecState::CAS);
16422
- }
16423
- }
16424
-
16425
- $this->state->unflag(MultiExecState::DISCARDED);
16426
- $this->state->flag(MultiExecState::INITIALIZED);
16427
- }
16428
-
16429
- /**
16430
- * Dynamically invokes a Redis command with the specified arguments.
16431
- *
16432
- * @param string $method Command ID.
16433
- * @param array $arguments Arguments for the command.
16434
- *
16435
- * @return mixed
16436
- */
16437
- public function __call($method, $arguments)
16438
- {
16439
- return $this->executeCommand(
16440
- $this->client->createCommand($method, $arguments)
16441
- );
16442
- }
16443
-
16444
- /**
16445
- * Executes a Redis command bypassing the transaction logic.
16446
- *
16447
- * @param string $commandID Command ID.
16448
- * @param array $arguments Arguments for the command.
16449
- *
16450
- * @throws ServerException
16451
- *
16452
- * @return mixed
16453
- */
16454
- protected function call($commandID, array $arguments = array())
16455
- {
16456
- $response = $this->client->executeCommand(
16457
- $this->client->createCommand($commandID, $arguments)
16458
- );
16459
-
16460
- if ($response instanceof ErrorResponseInterface) {
16461
- throw new ServerException($response->getMessage());
16462
- }
16463
-
16464
- return $response;
16465
- }
16466
-
16467
- /**
16468
- * Executes the specified Redis command.
16469
- *
16470
- * @param CommandInterface $command Command instance.
16471
- *
16472
- * @throws AbortedMultiExecException
16473
- * @throws CommunicationException
16474
- *
16475
- * @return $this|mixed
16476
- */
16477
- public function executeCommand(CommandInterface $command)
16478
- {
16479
- $this->initialize();
16480
-
16481
- if ($this->state->isCAS()) {
16482
- return $this->client->executeCommand($command);
16483
- }
16484
-
16485
- $response = $this->client->getConnection()->executeCommand($command);
16486
-
16487
- if ($response instanceof StatusResponse && $response == 'QUEUED') {
16488
- $this->commands->enqueue($command);
16489
- } elseif ($response instanceof ErrorResponseInterface) {
16490
- throw new AbortedMultiExecException($this, $response->getMessage());
16491
- } else {
16492
- $this->onProtocolError('The server did not return a +QUEUED status response.');
16493
- }
16494
-
16495
- return $this;
16496
- }
16497
-
16498
- /**
16499
- * Executes WATCH against one or more keys.
16500
- *
16501
- * @param string|array $keys One or more keys.
16502
- *
16503
- * @throws NotSupportedException
16504
- * @throws ClientException
16505
- *
16506
- * @return mixed
16507
- */
16508
- public function watch($keys)
16509
- {
16510
- if (!$this->client->getProfile()->supportsCommand('WATCH')) {
16511
- throw new NotSupportedException('WATCH is not supported by the current profile.');
16512
- }
16513
-
16514
- if ($this->state->isWatchAllowed()) {
16515
- throw new ClientException('Sending WATCH after MULTI is not allowed.');
16516
- }
16517
-
16518
- $response = $this->call('WATCH', is_array($keys) ? $keys : array($keys));
16519
- $this->state->flag(MultiExecState::WATCH);
16520
-
16521
- return $response;
16522
- }
16523
-
16524
- /**
16525
- * Finalizes the transaction by executing MULTI on the server.
16526
- *
16527
- * @return MultiExec
16528
- */
16529
- public function multi()
16530
- {
16531
- if ($this->state->check(MultiExecState::INITIALIZED | MultiExecState::CAS)) {
16532
- $this->state->unflag(MultiExecState::CAS);
16533
- $this->call('MULTI');
16534
- } else {
16535
- $this->initialize();
16536
- }
16537
-
16538
- return $this;
16539
- }
16540
-
16541
- /**
16542
- * Executes UNWATCH.
16543
- *
16544
- * @throws NotSupportedException
16545
- *
16546
- * @return MultiExec
16547
- */
16548
- public function unwatch()
16549
- {
16550
- if (!$this->client->getProfile()->supportsCommand('UNWATCH')) {
16551
- throw new NotSupportedException(
16552
- 'UNWATCH is not supported by the current profile.'
16553
- );
16554
- }
16555
-
16556
- $this->state->unflag(MultiExecState::WATCH);
16557
- $this->__call('UNWATCH', array());
16558
-
16559
- return $this;
16560
- }
16561
-
16562
- /**
16563
- * Resets the transaction by UNWATCH-ing the keys that are being WATCHed and
16564
- * DISCARD-ing pending commands that have been already sent to the server.
16565
- *
16566
- * @return MultiExec
16567
- */
16568
- public function discard()
16569
- {
16570
- if ($this->state->isInitialized()) {
16571
- $this->call($this->state->isCAS() ? 'UNWATCH' : 'DISCARD');
16572
-
16573
- $this->reset();
16574
- $this->state->flag(MultiExecState::DISCARDED);
16575
- }
16576
-
16577
- return $this;
16578
- }
16579
-
16580
- /**
16581
- * Executes the whole transaction.
16582
- *
16583
- * @return mixed
16584
- */
16585
- public function exec()
16586
- {
16587
- return $this->execute();
16588
- }
16589
-
16590
- /**
16591
- * Checks the state of the transaction before execution.
16592
- *
16593
- * @param mixed $callable Callback for execution.
16594
- *
16595
- * @throws \InvalidArgumentException
16596
- * @throws ClientException
16597
- */
16598
- private function checkBeforeExecution($callable)
16599
- {
16600
- if ($this->state->isExecuting()) {
16601
- throw new ClientException(
16602
- 'Cannot invoke "execute" or "exec" inside an active transaction context.'
16603
- );
16604
- }
16605
-
16606
- if ($callable) {
16607
- if (!is_callable($callable)) {
16608
- throw new \InvalidArgumentException('The argument must be a callable object.');
16609
- }
16610
-
16611
- if (!$this->commands->isEmpty()) {
16612
- $this->discard();
16613
-
16614
- throw new ClientException(
16615
- 'Cannot execute a transaction block after using fluent interface.'
16616
- );
16617
- }
16618
- } elseif ($this->attempts) {
16619
- $this->discard();
16620
-
16621
- throw new ClientException(
16622
- 'Automatic retries are supported only when a callable block is provided.'
16623
- );
16624
- }
16625
- }
16626
-
16627
- /**
16628
- * Handles the actual execution of the whole transaction.
16629
- *
16630
- * @param mixed $callable Optional callback for execution.
16631
- *
16632
- * @throws CommunicationException
16633
- * @throws AbortedMultiExecException
16634
- * @throws ServerException
16635
- *
16636
- * @return array
16637
- */
16638
- public function execute($callable = null)
16639
- {
16640
- $this->checkBeforeExecution($callable);
16641
-
16642
- $execResponse = null;
16643
- $attempts = $this->attempts;
16644
-
16645
- do {
16646
- if ($callable) {
16647
- $this->executeTransactionBlock($callable);
16648
- }
16649
-
16650
- if ($this->commands->isEmpty()) {
16651
- if ($this->state->isWatching()) {
16652
- $this->discard();
16653
- }
16654
-
16655
- return;
16656
- }
16657
-
16658
- $execResponse = $this->call('EXEC');
16659
-
16660
- if ($execResponse === null) {
16661
- if ($attempts === 0) {
16662
- throw new AbortedMultiExecException(
16663
- $this, 'The current transaction has been aborted by the server.'
16664
- );
16665
- }
16666
-
16667
- $this->reset();
16668
-
16669
- continue;
16670
- }
16671
-
16672
- break;
16673
- } while ($attempts-- > 0);
16674
-
16675
- $response = array();
16676
- $commands = $this->commands;
16677
- $size = count($execResponse);
16678
-
16679
- if ($size !== count($commands)) {
16680
- $this->onProtocolError('EXEC returned an unexpected number of response items.');
16681
- }
16682
-
16683
- for ($i = 0; $i < $size; ++$i) {
16684
- $cmdResponse = $execResponse[$i];
16685
-
16686
- if ($cmdResponse instanceof ErrorResponseInterface && $this->exceptions) {
16687
- throw new ServerException($cmdResponse->getMessage());
16688
- }
16689
-
16690
- $response[$i] = $commands->dequeue()->parseResponse($cmdResponse);
16691
- }
16692
-
16693
- return $response;
16694
- }
16695
-
16696
- /**
16697
- * Passes the current transaction object to a callable block for execution.
16698
- *
16699
- * @param mixed $callable Callback.
16700
- *
16701
- * @throws CommunicationException
16702
- * @throws ServerException
16703
- */
16704
- protected function executeTransactionBlock($callable)
16705
- {
16706
- $exception = null;
16707
- $this->state->flag(MultiExecState::INSIDEBLOCK);
16708
-
16709
- try {
16710
- call_user_func($callable, $this);
16711
- } catch (CommunicationException $exception) {
16712
- // NOOP
16713
- } catch (ServerException $exception) {
16714
- // NOOP
16715
- } catch (\Exception $exception) {
16716
- $this->discard();
16717
- }
16718
-
16719
- $this->state->unflag(MultiExecState::INSIDEBLOCK);
16720
-
16721
- if ($exception) {
16722
- throw $exception;
16723
- }
16724
- }
16725
-
16726
- /**
16727
- * Helper method for protocol errors encountered inside the transaction.
16728
- *
16729
- * @param string $message Error message.
16730
- */
16731
- private function onProtocolError($message)
16732
- {
16733
- // Since a MULTI/EXEC block cannot be initialized when using aggregate
16734
- // connections we can safely assume that Predis\Client::getConnection()
16735
- // will return a Predis\Connection\NodeConnectionInterface instance.
16736
- CommunicationException::handle(new ProtocolException(
16737
- $this->client->getConnection(), $message
16738
- ));
16739
- }
16740
- }
16741
-
16742
- /* --------------------------------------------------------------------------- */
16743
-
16744
- namespace Predis\Replication;
16745
-
16746
- use Predis\Command\CommandInterface;
16747
- use Predis\NotSupportedException;
16748
- use Predis\CommunicationException;
16749
- use Predis\ClientException;
16750
-
16751
- /**
16752
- * Defines a strategy for master/slave replication.
16753
- *
16754
- * @author Daniele Alessandri <suppakilla@gmail.com>
16755
- */
16756
- class ReplicationStrategy
16757
- {
16758
- protected $disallowed;
16759
- protected $readonly;
16760
- protected $readonlySHA1;
16761
-
16762
- /**
16763
- *
16764
- */
16765
- public function __construct()
16766
- {
16767
- $this->disallowed = $this->getDisallowedOperations();
16768
- $this->readonly = $this->getReadOnlyOperations();
16769
- $this->readonlySHA1 = array();
16770
- }
16771
-
16772
- /**
16773
- * Returns if the specified command will perform a read-only operation
16774
- * on Redis or not.
16775
- *
16776
- * @param CommandInterface $command Command instance.
16777
- *
16778
- * @throws NotSupportedException
16779
- *
16780
- * @return bool
16781
- */
16782
- public function isReadOperation(CommandInterface $command)
16783
- {
16784
- if (isset($this->disallowed[$id = $command->getId()])) {
16785
- throw new NotSupportedException(
16786
- "The command '$id' is not allowed in replication mode."
16787
- );
16788
- }
16789
-
16790
- if (isset($this->readonly[$id])) {
16791
- if (true === $readonly = $this->readonly[$id]) {
16792
- return true;
16793
- }
16794
-
16795
- return call_user_func($readonly, $command);
16796
- }
16797
-
16798
- if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
16799
- $sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0);
16800
-
16801
- if (isset($this->readonlySHA1[$sha1])) {
16802
- if (true === $readonly = $this->readonlySHA1[$sha1]) {
16803
- return true;
16804
- }
16805
-
16806
- return call_user_func($readonly, $command);
16807
- }
16808
- }
16809
-
16810
- return false;
16811
- }
16812
-
16813
- /**
16814
- * Returns if the specified command is not allowed for execution in a master
16815
- * / slave replication context.
16816
- *
16817
- * @param CommandInterface $command Command instance.
16818
- *
16819
- * @return bool
16820
- */
16821
- public function isDisallowedOperation(CommandInterface $command)
16822
- {
16823
- return isset($this->disallowed[$command->getId()]);
16824
- }
16825
-
16826
- /**
16827
- * Checks if a SORT command is a readable operation by parsing the arguments
16828
- * array of the specified commad instance.
16829
- *
16830
- * @param CommandInterface $command Command instance.
16831
- *
16832
- * @return bool
16833
- */
16834
- protected function isSortReadOnly(CommandInterface $command)
16835
- {
16836
- $arguments = $command->getArguments();
16837
- $argc = count($arguments);
16838
-
16839
- if ($argc > 1) {
16840
- for ($i = 1; $i < $argc; ++$i) {
16841
- $argument = strtoupper($arguments[$i]);
16842
- if ($argument === 'STORE') {
16843
- return false;
16844
- }
16845
- }
16846
- }
16847
-
16848
- return true;
16849
- }
16850
-
16851
- /**
16852
- * Checks if BITFIELD performs a read-only operation by looking for certain
16853
- * SET and INCRYBY modifiers in the arguments array of the command.
16854
- *
16855
- * @param CommandInterface $command Command instance.
16856
- *
16857
- * @return bool
16858
- */
16859
- protected function isBitfieldReadOnly(CommandInterface $command)
16860
- {
16861
- $arguments = $command->getArguments();
16862
- $argc = count($arguments);
16863
-
16864
- if ($argc >= 2) {
16865
- for ($i = 1; $i < $argc; ++$i) {
16866
- $argument = strtoupper($arguments[$i]);
16867
- if ($argument === 'SET' || $argument === 'INCRBY') {
16868
- return false;
16869
- }
16870
- }
16871
- }
16872
-
16873
- return true;
16874
- }
16875
-
16876
- /**
16877
- * Checks if a GEORADIUS command is a readable operation by parsing the
16878
- * arguments array of the specified commad instance.
16879
- *
16880
- * @param CommandInterface $command Command instance.
16881
- *
16882
- * @return bool
16883
- */
16884
- protected function isGeoradiusReadOnly(CommandInterface $command)
16885
- {
16886
- $arguments = $command->getArguments();
16887
- $argc = count($arguments);
16888
- $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
16889
-
16890
- if ($argc > $startIndex) {
16891
- for ($i = $startIndex; $i < $argc; ++$i) {
16892
- $argument = strtoupper($arguments[$i]);
16893
- if ($argument === 'STORE' || $argument === 'STOREDIST') {
16894
- return false;
16895
- }
16896
- }
16897
- }
16898
-
16899
- return true;
16900
- }
16901
-
16902
- /**
16903
- * Marks a command as a read-only operation.
16904
- *
16905
- * When the behavior of a command can be decided only at runtime depending
16906
- * on its arguments, a callable object can be provided to dynamically check
16907
- * if the specified command performs a read or a write operation.
16908
- *
16909
- * @param string $commandID Command ID.
16910
- * @param mixed $readonly A boolean value or a callable object.
16911
- */
16912
- public function setCommandReadOnly($commandID, $readonly = true)
16913
- {
16914
- $commandID = strtoupper($commandID);
16915
-
16916
- if ($readonly) {
16917
- $this->readonly[$commandID] = $readonly;
16918
- } else {
16919
- unset($this->readonly[$commandID]);
16920
- }
16921
- }
16922
-
16923
- /**
16924
- * Marks a Lua script for EVAL and EVALSHA as a read-only operation. When
16925
- * the behaviour of a script can be decided only at runtime depending on
16926
- * its arguments, a callable object can be provided to dynamically check
16927
- * if the passed instance of EVAL or EVALSHA performs write operations or
16928
- * not.
16929
- *
16930
- * @param string $script Body of the Lua script.
16931
- * @param mixed $readonly A boolean value or a callable object.
16932
- */
16933
- public function setScriptReadOnly($script, $readonly = true)
16934
- {
16935
- $sha1 = sha1($script);
16936
-
16937
- if ($readonly) {
16938
- $this->readonlySHA1[$sha1] = $readonly;
16939
- } else {
16940
- unset($this->readonlySHA1[$sha1]);
16941
- }
16942
- }
16943
-
16944
- /**
16945
- * Returns the default list of disallowed commands.
16946
- *
16947
- * @return array
16948
- */
16949
- protected function getDisallowedOperations()
16950
- {
16951
- return array(
16952
- 'SHUTDOWN' => true,
16953
- 'INFO' => true,
16954
- 'DBSIZE' => true,
16955
- 'LASTSAVE' => true,
16956
- 'CONFIG' => true,
16957
- 'MONITOR' => true,
16958
- 'SLAVEOF' => true,
16959
- 'SAVE' => true,
16960
- 'BGSAVE' => true,
16961
- 'BGREWRITEAOF' => true,
16962
- 'SLOWLOG' => true,
16963
- );
16964
- }
16965
-
16966
- /**
16967
- * Returns the default list of commands performing read-only operations.
16968
- *
16969
- * @return array
16970
- */
16971
- protected function getReadOnlyOperations()
16972
- {
16973
- return array(
16974
- 'EXISTS' => true,
16975
- 'TYPE' => true,
16976
- 'KEYS' => true,
16977
- 'SCAN' => true,
16978
- 'RANDOMKEY' => true,
16979
- 'TTL' => true,
16980
- 'GET' => true,
16981
- 'MGET' => true,
16982
- 'SUBSTR' => true,
16983
- 'STRLEN' => true,
16984
- 'GETRANGE' => true,
16985
- 'GETBIT' => true,
16986
- 'LLEN' => true,
16987
- 'LRANGE' => true,
16988
- 'LINDEX' => true,
16989
- 'SCARD' => true,
16990
- 'SISMEMBER' => true,
16991
- 'SINTER' => true,
16992
- 'SUNION' => true,
16993
- 'SDIFF' => true,
16994
- 'SMEMBERS' => true,
16995
- 'SSCAN' => true,
16996
- 'SRANDMEMBER' => true,
16997
- 'ZRANGE' => true,
16998
- 'ZREVRANGE' => true,
16999
- 'ZRANGEBYSCORE' => true,
17000
- 'ZREVRANGEBYSCORE' => true,
17001
- 'ZCARD' => true,
17002
- 'ZSCORE' => true,
17003
- 'ZCOUNT' => true,
17004
- 'ZRANK' => true,
17005
- 'ZREVRANK' => true,
17006
- 'ZSCAN' => true,
17007
- 'ZLEXCOUNT' => true,
17008
- 'ZRANGEBYLEX' => true,
17009
- 'ZREVRANGEBYLEX' => true,
17010
- 'HGET' => true,
17011
- 'HMGET' => true,
17012
- 'HEXISTS' => true,
17013
- 'HLEN' => true,
17014
- 'HKEYS' => true,
17015
- 'HVALS' => true,
17016
- 'HGETALL' => true,
17017
- 'HSCAN' => true,
17018
- 'HSTRLEN' => true,
17019
- 'PING' => true,
17020
- 'AUTH' => true,
17021
- 'SELECT' => true,
17022
- 'ECHO' => true,
17023
- 'QUIT' => true,
17024
- 'OBJECT' => true,
17025
- 'BITCOUNT' => true,
17026
- 'BITPOS' => true,
17027
- 'TIME' => true,
17028
- 'PFCOUNT' => true,
17029
- 'SORT' => array($this, 'isSortReadOnly'),
17030
- 'BITFIELD' => array($this, 'isBitfieldReadOnly'),
17031
- 'GEOHASH' => true,
17032
- 'GEOPOS' => true,
17033
- 'GEODIST' => true,
17034
- 'GEORADIUS' => array($this, 'isGeoradiusReadOnly'),
17035
- 'GEORADIUSBYMEMBER' => array($this, 'isGeoradiusReadOnly'),
17036
- );
17037
- }
17038
- }
17039
-
17040
- /**
17041
- * Exception class that identifies a role mismatch when connecting to node
17042
- * managed by redis-sentinel.
17043
- *
17044
- * @author Daniele Alessandri <suppakilla@gmail.com>
17045
- */
17046
- class RoleException extends CommunicationException
17047
- {
17048
- }
17049
-
17050
- /**
17051
- * Exception class that identifies when master is missing in a replication setup.
17052
- *
17053
- * @author Daniele Alessandri <suppakilla@gmail.com>
17054
- */
17055
- class MissingMasterException extends ClientException
17056
- {
17057
- }
17058
-
17059
- /* --------------------------------------------------------------------------- */
17060
-
17061
- namespace Predis\Monitor;
17062
-
17063
- use Predis\ClientInterface;
17064
- use Predis\Connection\AggregateConnectionInterface;
17065
- use Predis\NotSupportedException;
17066
-
17067
- /**
17068
- * Redis MONITOR consumer.
17069
- *
17070
- * @author Daniele Alessandri <suppakilla@gmail.com>
17071
- */
17072
- class Consumer implements \Iterator
17073
- {
17074
- private $client;
17075
- private $valid;
17076
- private $position;
17077
-
17078
- /**
17079
- * @param ClientInterface $client Client instance used by the consumer.
17080
- */
17081
- public function __construct(ClientInterface $client)
17082
- {
17083
- $this->assertClient($client);
17084
-
17085
- $this->client = $client;
17086
-
17087
- $this->start();
17088
- }
17089
-
17090
- /**
17091
- * Automatically stops the consumer when the garbage collector kicks in.
17092
- */
17093
- public function __destruct()
17094
- {
17095
- $this->stop();
17096
- }
17097
-
17098
- /**
17099
- * Checks if the passed client instance satisfies the required conditions
17100
- * needed to initialize a monitor consumer.
17101
- *
17102
- * @param ClientInterface $client Client instance used by the consumer.
17103
- *
17104
- * @throws NotSupportedException
17105
- */
17106
- private function assertClient(ClientInterface $client)
17107
- {
17108
- if ($client->getConnection() instanceof AggregateConnectionInterface) {
17109
- throw new NotSupportedException(
17110
- 'Cannot initialize a monitor consumer over aggregate connections.'
17111
- );
17112
- }
17113
-
17114
- if ($client->getProfile()->supportsCommand('MONITOR') === false) {
17115
- throw new NotSupportedException("The current profile does not support 'MONITOR'.");
17116
- }
17117
- }
17118
-
17119
- /**
17120
- * Initializes the consumer and sends the MONITOR command to the server.
17121
- */
17122
- protected function start()
17123
- {
17124
- $this->client->executeCommand(
17125
- $this->client->createCommand('MONITOR')
17126
- );
17127
- $this->valid = true;
17128
- }
17129
-
17130
- /**
17131
- * Stops the consumer. Internally this is done by disconnecting from server
17132
- * since there is no way to terminate the stream initialized by MONITOR.
17133
- */
17134
- public function stop()
17135
- {
17136
- $this->client->disconnect();
17137
- $this->valid = false;
17138
- }
17139
-
17140
- /**
17141
- * {@inheritdoc}
17142
- */
17143
- public function rewind()
17144
- {
17145
- // NOOP
17146
- }
17147
-
17148
- /**
17149
- * Returns the last message payload retrieved from the server.
17150
- *
17151
- * @return object
17152
- */
17153
- public function current()
17154
- {
17155
- return $this->getValue();
17156
- }
17157
-
17158
- /**
17159
- * {@inheritdoc}
17160
- */
17161
- public function key()
17162
- {
17163
- return $this->position;
17164
- }
17165
-
17166
- /**
17167
- * {@inheritdoc}
17168
- */
17169
- public function next()
17170
- {
17171
- ++$this->position;
17172
- }
17173
-
17174
- /**
17175
- * Checks if the the consumer is still in a valid state to continue.
17176
- *
17177
- * @return bool
17178
- */
17179
- public function valid()
17180
- {
17181
- return $this->valid;
17182
- }
17183
-
17184
- /**
17185
- * Waits for a new message from the server generated by MONITOR and returns
17186
- * it when available.
17187
- *
17188
- * @return object
17189
- */
17190
- private function getValue()
17191
- {
17192
- $database = 0;
17193
- $client = null;
17194
- $event = $this->client->getConnection()->read();
17195
-
17196
- $callback = function ($matches) use (&$database, &$client) {
17197
- if (2 === $count = count($matches)) {
17198
- // Redis <= 2.4
17199
- $database = (int) $matches[1];
17200
- }
17201
-
17202
- if (4 === $count) {
17203
- // Redis >= 2.6
17204
- $database = (int) $matches[2];
17205
- $client = $matches[3];
17206
- }
17207
-
17208
- return ' ';
17209
- };
17210
-
17211
- $event = preg_replace_callback('/ \(db (\d+)\) | \[(\d+) (.*?)\] /', $callback, $event, 1);
17212
- @list($timestamp, $command, $arguments) = explode(' ', $event, 3);
17213
-
17214
- return (object) array(
17215
- 'timestamp' => (float) $timestamp,
17216
- 'database' => $database,
17217
- 'client' => $client,
17218
- 'command' => substr($command, 1, -1),
17219
- 'arguments' => $arguments,
17220
- );
17221
- }
17222
- }
17223
-
17224
- /* --------------------------------------------------------------------------- */
17225
-
17226
- namespace Predis\Session;
17227
-
17228
- use Predis\ClientInterface;
17229
-
17230
- /**
17231
- * Session handler class that relies on Predis\Client to store PHP's sessions
17232
- * data into one or multiple Redis servers.
17233
- *
17234
- * This class is mostly intended for PHP 5.4 but it can be used under PHP 5.3
17235
- * provided that a polyfill for `SessionHandlerInterface` is defined by either
17236
- * you or an external package such as `symfony/http-foundation`.
17237
- *
17238
- * @author Daniele Alessandri <suppakilla@gmail.com>
17239
- */
17240
- class Handler implements \SessionHandlerInterface
17241
- {
17242
- protected $client;
17243
- protected $ttl;
17244
-
17245
- /**
17246
- * @param ClientInterface $client Fully initialized client instance.
17247
- * @param array $options Session handler options.
17248
- */
17249
- public function __construct(ClientInterface $client, array $options = array())
17250
- {
17251
- $this->client = $client;
17252
-
17253
- if (isset($options['gc_maxlifetime'])) {
17254
- $this->ttl = (int) $options['gc_maxlifetime'];
17255
- } else {
17256
- $this->ttl = ini_get('session.gc_maxlifetime');
17257
- }
17258
- }
17259
-
17260
- /**
17261
- * Registers this instance as the current session handler.
17262
- */
17263
- public function register()
17264
- {
17265
- if (PHP_VERSION_ID >= 50400) {
17266
- session_set_save_handler($this, true);
17267
- } else {
17268
- session_set_save_handler(
17269
- array($this, 'open'),
17270
- array($this, 'close'),
17271
- array($this, 'read'),
17272
- array($this, 'write'),
17273
- array($this, 'destroy'),
17274
- array($this, 'gc')
17275
- );
17276
- }
17277
- }
17278
-
17279
- /**
17280
- * {@inheritdoc}
17281
- */
17282
- public function open($save_path, $session_id)
17283
- {
17284
- // NOOP
17285
- return true;
17286
- }
17287
-
17288
- /**
17289
- * {@inheritdoc}
17290
- */
17291
- public function close()
17292
- {
17293
- // NOOP
17294
- return true;
17295
- }
17296
-
17297
- /**
17298
- * {@inheritdoc}
17299
- */
17300
- public function gc($maxlifetime)
17301
- {
17302
- // NOOP
17303
- return true;
17304
- }
17305
-
17306
- /**
17307
- * {@inheritdoc}
17308
- */
17309
- public function read($session_id)
17310
- {
17311
- if ($data = $this->client->get($session_id)) {
17312
- return $data;
17313
- }
17314
-
17315
- return '';
17316
- }
17317
- /**
17318
- * {@inheritdoc}
17319
- */
17320
- public function write($session_id, $session_data)
17321
- {
17322
- $this->client->setex($session_id, $this->ttl, $session_data);
17323
-
17324
- return true;
17325
- }
17326
-
17327
- /**
17328
- * {@inheritdoc}
17329
- */
17330
- public function destroy($session_id)
17331
- {
17332
- $this->client->del($session_id);
17333
-
17334
- return true;
17335
- }
17336
-
17337
- /**
17338
- * Returns the underlying client instance.
17339
- *
17340
- * @return ClientInterface
17341
- */
17342
- public function getClient()
17343
- {
17344
- return $this->client;
17345
- }
17346
-
17347
- /**
17348
- * Returns the session max lifetime value.
17349
- *
17350
- * @return int
17351
- */
17352
- public function getMaxLifeTime()
17353
- {
17354
- return $this->ttl;
17355
- }
17356
- }
17357
-
17358
- /* --------------------------------------------------------------------------- */
17359
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/predis/LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2009-2016 Daniele Alessandri
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
includes/predis/VERSION ADDED
@@ -0,0 +1 @@
 
1
+ 1.1.1
includes/predis/autoload.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ require __DIR__.'/src/Autoloader.php';
13
+
14
+ Predis\Autoloader::register();
includes/predis/src/Autoloader.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ /**
15
+ * Implements a lightweight PSR-0 compliant autoloader for Predis.
16
+ *
17
+ * @author Eric Naeseth <eric@thumbtack.com>
18
+ * @author Daniele Alessandri <suppakilla@gmail.com>
19
+ */
20
+ class Autoloader
21
+ {
22
+ private $directory;
23
+ private $prefix;
24
+ private $prefixLength;
25
+
26
+ /**
27
+ * @param string $baseDirectory Base directory where the source files are located.
28
+ */
29
+ public function __construct($baseDirectory = __DIR__)
30
+ {
31
+ $this->directory = $baseDirectory;
32
+ $this->prefix = __NAMESPACE__.'\\';
33
+ $this->prefixLength = strlen($this->prefix);
34
+ }
35
+
36
+ /**
37
+ * Registers the autoloader class with the PHP SPL autoloader.
38
+ *
39
+ * @param bool $prepend Prepend the autoloader on the stack instead of appending it.
40
+ */
41
+ public static function register($prepend = false)
42
+ {
43
+ spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
44
+ }
45
+
46
+ /**
47
+ * Loads a class from a file using its fully qualified name.
48
+ *
49
+ * @param string $className Fully qualified name of a class.
50
+ */
51
+ public function autoload($className)
52
+ {
53
+ if (0 === strpos($className, $this->prefix)) {
54
+ $parts = explode('\\', substr($className, $this->prefixLength));
55
+ $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
56
+
57
+ if (is_file($filepath)) {
58
+ require $filepath;
59
+ }
60
+ }
61
+ }
62
+ }
includes/predis/src/Client.php ADDED
@@ -0,0 +1,547 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Command\RawCommand;
16
+ use Predis\Command\ScriptCommand;
17
+ use Predis\Configuration\Options;
18
+ use Predis\Configuration\OptionsInterface;
19
+ use Predis\Connection\AggregateConnectionInterface;
20
+ use Predis\Connection\ConnectionInterface;
21
+ use Predis\Connection\ParametersInterface;
22
+ use Predis\Monitor\Consumer as MonitorConsumer;
23
+ use Predis\Pipeline\Pipeline;
24
+ use Predis\PubSub\Consumer as PubSubConsumer;
25
+ use Predis\Response\ErrorInterface as ErrorResponseInterface;
26
+ use Predis\Response\ResponseInterface;
27
+ use Predis\Response\ServerException;
28
+ use Predis\Transaction\MultiExec as MultiExecTransaction;
29
+
30
+ /**
31
+ * Client class used for connecting and executing commands on Redis.
32
+ *
33
+ * This is the main high-level abstraction of Predis upon which various other
34
+ * abstractions are built. Internally it aggregates various other classes each
35
+ * one with its own responsibility and scope.
36
+ *
37
+ * {@inheritdoc}
38
+ *
39
+ * @author Daniele Alessandri <suppakilla@gmail.com>
40
+ */
41
+ class Client implements ClientInterface, \IteratorAggregate
42
+ {
43
+ const VERSION = '1.1.1';
44
+
45
+ protected $connection;
46
+ protected $options;
47
+ private $profile;
48
+
49
+ /**
50
+ * @param mixed $parameters Connection parameters for one or more servers.
51
+ * @param mixed $options Options to configure some behaviours of the client.
52
+ */
53
+ public function __construct($parameters = null, $options = null)
54
+ {
55
+ $this->options = $this->createOptions($options ?: array());
56
+ $this->connection = $this->createConnection($parameters ?: array());
57
+ $this->profile = $this->options->profile;
58
+ }
59
+
60
+ /**
61
+ * Creates a new instance of Predis\Configuration\Options from different
62
+ * types of arguments or simply returns the passed argument if it is an
63
+ * instance of Predis\Configuration\OptionsInterface.
64
+ *
65
+ * @param mixed $options Client options.
66
+ *
67
+ * @throws \InvalidArgumentException
68
+ *
69
+ * @return OptionsInterface
70
+ */
71
+ protected function createOptions($options)
72
+ {
73
+ if (is_array($options)) {
74
+ return new Options($options);
75
+ }
76
+
77
+ if ($options instanceof OptionsInterface) {
78
+ return $options;
79
+ }
80
+
81
+ throw new \InvalidArgumentException('Invalid type for client options.');
82
+ }
83
+
84
+ /**
85
+ * Creates single or aggregate connections from different types of arguments
86
+ * (string, array) or returns the passed argument if it is an instance of a
87
+ * class implementing Predis\Connection\ConnectionInterface.
88
+ *
89
+ * Accepted types for connection parameters are:
90
+ *
91
+ * - Instance of Predis\Connection\ConnectionInterface.
92
+ * - Instance of Predis\Connection\ParametersInterface.
93
+ * - Array
94
+ * - String
95
+ * - Callable
96
+ *
97
+ * @param mixed $parameters Connection parameters or connection instance.
98
+ *
99
+ * @throws \InvalidArgumentException
100
+ *
101
+ * @return ConnectionInterface
102
+ */
103
+ protected function createConnection($parameters)
104
+ {
105
+ if ($parameters instanceof ConnectionInterface) {
106
+ return $parameters;
107
+ }
108
+
109
+ if ($parameters instanceof ParametersInterface || is_string($parameters)) {
110
+ return $this->options->connections->create($parameters);
111
+ }
112
+
113
+ if (is_array($parameters)) {
114
+ if (!isset($parameters[0])) {
115
+ return $this->options->connections->create($parameters);
116
+ }
117
+
118
+ $options = $this->options;
119
+
120
+ if ($options->defined('aggregate')) {
121
+ $initializer = $this->getConnectionInitializerWrapper($options->aggregate);
122
+ $connection = $initializer($parameters, $options);
123
+ } elseif ($options->defined('replication')) {
124
+ $replication = $options->replication;
125
+
126
+ if ($replication instanceof AggregateConnectionInterface) {
127
+ $connection = $replication;
128
+ $options->connections->aggregate($connection, $parameters);
129
+ } else {
130
+ $initializer = $this->getConnectionInitializerWrapper($replication);
131
+ $connection = $initializer($parameters, $options);
132
+ }
133
+ } else {
134
+ $connection = $options->cluster;
135
+ $options->connections->aggregate($connection, $parameters);
136
+ }
137
+
138
+ return $connection;
139
+ }
140
+
141
+ if (is_callable($parameters)) {
142
+ $initializer = $this->getConnectionInitializerWrapper($parameters);
143
+ $connection = $initializer($this->options);
144
+
145
+ return $connection;
146
+ }
147
+
148
+ throw new \InvalidArgumentException('Invalid type for connection parameters.');
149
+ }
150
+
151
+ /**
152
+ * Wraps a callable to make sure that its returned value represents a valid
153
+ * connection type.
154
+ *
155
+ * @param mixed $callable
156
+ *
157
+ * @return \Closure
158
+ */
159
+ protected function getConnectionInitializerWrapper($callable)
160
+ {
161
+ return function () use ($callable) {
162
+ $connection = call_user_func_array($callable, func_get_args());
163
+
164
+ if (!$connection instanceof ConnectionInterface) {
165
+ throw new \UnexpectedValueException(
166
+ 'The callable connection initializer returned an invalid type.'
167
+ );
168
+ }
169
+
170
+ return $connection;
171
+ };
172
+ }
173
+
174
+ /**
175
+ * {@inheritdoc}
176
+ */
177
+ public function getProfile()
178
+ {
179
+ return $this->profile;
180
+ }
181
+
182
+ /**
183
+ * {@inheritdoc}
184
+ */
185
+ public function getOptions()
186
+ {
187
+ return $this->options;
188
+ }
189
+
190
+ /**
191
+ * Creates a new client instance for the specified connection ID or alias,
192
+ * only when working with an aggregate connection (cluster, replication).
193
+ * The new client instances uses the same options of the original one.
194
+ *
195
+ * @param string $connectionID Identifier of a connection.
196
+ *
197
+ * @throws \InvalidArgumentException
198
+ *
199
+ * @return Client
200
+ */
201
+ public function getClientFor($connectionID)
202
+ {
203
+ if (!$connection = $this->getConnectionById($connectionID)) {
204
+ throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
205
+ }
206
+
207
+ return new static($connection, $this->options);
208
+ }
209
+
210
+ /**
211
+ * Opens the underlying connection and connects to the server.
212
+ */
213
+ public function connect()
214
+ {
215
+ $this->connection->connect();
216
+ }
217
+
218
+ /**
219
+ * Closes the underlying connection and disconnects from the server.
220
+ */
221
+ public function disconnect()
222
+ {
223
+ $this->connection->disconnect();
224
+ }
225
+
226
+ /**
227
+ * Closes the underlying connection and disconnects from the server.
228
+ *
229
+ * This is the same as `Client::disconnect()` as it does not actually send
230
+ * the `QUIT` command to Redis, but simply closes the connection.
231
+ */
232
+ public function quit()
233
+ {
234
+ $this->disconnect();
235
+ }
236
+
237
+ /**
238
+ * Returns the current state of the underlying connection.
239
+ *
240
+ * @return bool
241
+ */
242
+ public function isConnected()
243
+ {
244
+ return $this->connection->isConnected();
245
+ }
246
+
247
+ /**
248
+ * {@inheritdoc}
249
+ */
250
+ public function getConnection()
251
+ {
252
+ return $this->connection;
253
+ }
254
+
255
+ /**
256
+ * Retrieves the specified connection from the aggregate connection when the
257
+ * client is in cluster or replication mode.
258
+ *
259
+ * @param string $connectionID Index or alias of the single connection.
260
+ *
261
+ * @throws NotSupportedException
262
+ *
263
+ * @return Connection\NodeConnectionInterface
264
+ */
265
+ public function getConnectionById($connectionID)
266
+ {
267
+ if (!$this->connection instanceof AggregateConnectionInterface) {
268
+ throw new NotSupportedException(
269
+ 'Retrieving connections by ID is supported only by aggregate connections.'
270
+ );
271
+ }
272
+
273
+ return $this->connection->getConnectionById($connectionID);
274
+ }
275
+
276
+ /**
277
+ * Executes a command without filtering its arguments, parsing the response,
278
+ * applying any prefix to keys or throwing exceptions on Redis errors even
279
+ * regardless of client options.
280
+ *
281
+ * It is possible to identify Redis error responses from normal responses
282
+ * using the second optional argument which is populated by reference.
283
+ *
284
+ * @param array $arguments Command arguments as defined by the command signature.
285
+ * @param bool $error Set to TRUE when Redis returned an error response.
286
+ *
287
+ * @return mixed
288
+ */
289
+ public function executeRaw(array $arguments, &$error = null)
290
+ {
291
+ $error = false;
292
+
293
+ $response = $this->connection->executeCommand(
294
+ new RawCommand($arguments)
295
+ );
296
+
297
+ if ($response instanceof ResponseInterface) {
298
+ if ($response instanceof ErrorResponseInterface) {
299
+ $error = true;
300
+ }
301
+
302
+ return (string) $response;
303
+ }
304
+
305
+ return $response;
306
+ }
307
+
308
+ /**
309
+ * {@inheritdoc}
310
+ */
311
+ public function __call($commandID, $arguments)
312
+ {
313
+ return $this->executeCommand(
314
+ $this->createCommand($commandID, $arguments)
315
+ );
316
+ }
317
+
318
+ /**
319
+ * {@inheritdoc}
320
+ */
321
+ public function createCommand($commandID, $arguments = array())
322
+ {
323
+ return $this->profile->createCommand($commandID, $arguments);
324
+ }
325
+
326
+ /**
327
+ * {@inheritdoc}
328
+ */
329
+ public function executeCommand(CommandInterface $command)
330
+ {
331
+ $response = $this->connection->executeCommand($command);
332
+
333
+ if ($response instanceof ResponseInterface) {
334
+ if ($response instanceof ErrorResponseInterface) {
335
+ $response = $this->onErrorResponse($command, $response);
336
+ }
337
+
338
+ return $response;
339
+ }
340
+
341
+ return $command->parseResponse($response);
342
+ }
343
+
344
+ /**
345
+ * Handles -ERR responses returned by Redis.
346
+ *
347
+ * @param CommandInterface $command Redis command that generated the error.
348
+ * @param ErrorResponseInterface $response Instance of the error response.
349
+ *
350
+ * @throws ServerException
351
+ *
352
+ * @return mixed
353
+ */
354
+ protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
355
+ {
356
+ if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
357
+ $eval = $this->createCommand('EVAL');
358
+ $eval->setRawArguments($command->getEvalArguments());
359
+
360
+ $response = $this->executeCommand($eval);
361
+
362
+ if (!$response instanceof ResponseInterface) {
363
+ $response = $command->parseResponse($response);
364
+ }
365
+
366
+ return $response;
367
+ }
368
+
369
+ if ($this->options->exceptions) {
370
+ throw new ServerException($response->getMessage());
371
+ }
372
+
373
+ return $response;
374
+ }
375
+
376
+ /**
377
+ * Executes the specified initializer method on `$this` by adjusting the
378
+ * actual invokation depending on the arity (0, 1 or 2 arguments). This is
379
+ * simply an utility method to create Redis contexts instances since they
380
+ * follow a common initialization path.
381
+ *
382
+ * @param string $initializer Method name.
383
+ * @param array $argv Arguments for the method.
384
+ *
385
+ * @return mixed
386
+ */
387
+ private function sharedContextFactory($initializer, $argv = null)
388
+ {
389
+ switch (count($argv)) {
390
+ case 0:
391
+ return $this->$initializer();
392
+
393
+ case 1:
394
+ return is_array($argv[0])
395
+ ? $this->$initializer($argv[0])
396
+ : $this->$initializer(null, $argv[0]);
397
+
398
+ case 2:
399
+ list($arg0, $arg1) = $argv;
400
+
401
+ return $this->$initializer($arg0, $arg1);
402
+
403
+ default:
404
+ return $this->$initializer($this, $argv);
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Creates a new pipeline context and returns it, or returns the results of
410
+ * a pipeline executed inside the optionally provided callable object.
411
+ *
412
+ * @param mixed ... Array of options, a callable for execution, or both.
413
+ *
414
+ * @return Pipeline|array
415
+ */
416
+ public function pipeline(/* arguments */)
417
+ {
418
+ return $this->sharedContextFactory('createPipeline', func_get_args());
419
+ }
420
+
421
+ /**
422
+ * Actual pipeline context initializer method.
423
+ *
424
+ * @param array $options Options for the context.
425
+ * @param mixed $callable Optional callable used to execute the context.
426
+ *
427
+ * @return Pipeline|array
428
+ */
429
+ protected function createPipeline(array $options = null, $callable = null)
430
+ {
431
+ if (isset($options['atomic']) && $options['atomic']) {
432
+ $class = 'Predis\Pipeline\Atomic';
433
+ } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
434
+ $class = 'Predis\Pipeline\FireAndForget';
435
+ } else {
436
+ $class = 'Predis\Pipeline\Pipeline';
437
+ }
438
+
439
+ /*
440
+ * @var ClientContextInterface
441
+ */
442
+ $pipeline = new $class($this);
443
+
444
+ if (isset($callable)) {
445
+ return $pipeline->execute($callable);
446
+ }
447
+
448
+ return $pipeline;
449
+ }
450
+
451
+ /**
452
+ * Creates a new transaction context and returns it, or returns the results
453
+ * of a transaction executed inside the optionally provided callable object.
454
+ *
455
+ * @param mixed ... Array of options, a callable for execution, or both.
456
+ *
457
+ * @return MultiExecTransaction|array
458
+ */
459
+ public function transaction(/* arguments */)
460
+ {
461
+ return $this->sharedContextFactory('createTransaction', func_get_args());
462
+ }
463
+
464
+ /**
465
+ * Actual transaction context initializer method.
466
+ *
467
+ * @param array $options Options for the context.
468
+ * @param mixed $callable Optional callable used to execute the context.
469
+ *
470
+ * @return MultiExecTransaction|array
471
+ */
472
+ protected function createTransaction(array $options = null, $callable = null)
473
+ {
474
+ $transaction = new MultiExecTransaction($this, $options);
475
+
476
+ if (isset($callable)) {
477
+ return $transaction->execute($callable);
478
+ }
479
+
480
+ return $transaction;
481
+ }
482
+
483
+ /**
484
+ * Creates a new publish/subscribe context and returns it, or starts its loop
485
+ * inside the optionally provided callable object.
486
+ *
487
+ * @param mixed ... Array of options, a callable for execution, or both.
488
+ *
489
+ * @return PubSubConsumer|null
490
+ */
491
+ public function pubSubLoop(/* arguments */)
492
+ {
493
+ return $this->sharedContextFactory('createPubSub', func_get_args());
494
+ }
495
+
496
+ /**
497
+ * Actual publish/subscribe context initializer method.
498
+ *
499
+ * @param array $options Options for the context.
500
+ * @param mixed $callable Optional callable used to execute the context.
501
+ *
502
+ * @return PubSubConsumer|null
503
+ */
504
+ protected function createPubSub(array $options = null, $callable = null)
505
+ {
506
+ $pubsub = new PubSubConsumer($this, $options);
507
+
508
+ if (!isset($callable)) {
509
+ return $pubsub;
510
+ }
511
+
512
+ foreach ($pubsub as $message) {
513
+ if (call_user_func($callable, $pubsub, $message) === false) {
514
+ $pubsub->stop();
515
+ }
516
+ }
517
+ }
518
+
519
+ /**
520
+ * Creates a new monitor consumer and returns it.
521
+ *
522
+ * @return MonitorConsumer
523
+ */
524
+ public function monitor()
525
+ {
526
+ return new MonitorConsumer($this);
527
+ }
528
+
529
+ /**
530
+ * {@inheritdoc}
531
+ */
532
+ public function getIterator()
533
+ {
534
+ $clients = array();
535
+ $connection = $this->getConnection();
536
+
537
+ if (!$connection instanceof \Traversable) {
538
+ throw new ClientException('The underlying connection is not traversable');
539
+ }
540
+
541
+ foreach ($connection as $node) {
542
+ $clients[(string) $node] = new static($node, $this->getOptions());
543
+ }
544
+
545
+ return new \ArrayIterator($clients);
546
+ }
547
+ }
includes/predis/src/ClientContextInterface.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * Interface defining a client-side context such as a pipeline or transaction.
18
+ *
19
+ * @method $this del(array $keys)
20
+ * @method $this dump($key)
21
+ * @method $this exists($key)
22
+ * @method $this expire($key, $seconds)
23
+ * @method $this expireat($key, $timestamp)
24
+ * @method $this keys($pattern)
25
+ * @method $this move($key, $db)
26
+ * @method $this object($subcommand, $key)
27
+ * @method $this persist($key)
28
+ * @method $this pexpire($key, $milliseconds)
29
+ * @method $this pexpireat($key, $timestamp)
30
+ * @method $this pttl($key)
31
+ * @method $this randomkey()
32
+ * @method $this rename($key, $target)
33
+ * @method $this renamenx($key, $target)
34
+ * @method $this scan($cursor, array $options = null)
35
+ * @method $this sort($key, array $options = null)
36
+ * @method $this ttl($key)
37
+ * @method $this type($key)
38
+ * @method $this append($key, $value)
39
+ * @method $this bitcount($key, $start = null, $end = null)
40
+ * @method $this bitop($operation, $destkey, $key)
41
+ * @method $this bitfield($key, $subcommand, ...$subcommandArg)
42
+ * @method $this decr($key)
43
+ * @method $this decrby($key, $decrement)
44
+ * @method $this get($key)
45
+ * @method $this getbit($key, $offset)
46
+ * @method $this getrange($key, $start, $end)
47
+ * @method $this getset($key, $value)
48
+ * @method $this incr($key)
49
+ * @method $this incrby($key, $increment)
50
+ * @method $this incrbyfloat($key, $increment)
51
+ * @method $this mget(array $keys)
52
+ * @method $this mset(array $dictionary)
53
+ * @method $this msetnx(array $dictionary)
54
+ * @method $this psetex($key, $milliseconds, $value)
55
+ * @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
56
+ * @method $this setbit($key, $offset, $value)
57
+ * @method $this setex($key, $seconds, $value)
58
+ * @method $this setnx($key, $value)
59
+ * @method $this setrange($key, $offset, $value)
60
+ * @method $this strlen($key)
61
+ * @method $this hdel($key, array $fields)
62
+ * @method $this hexists($key, $field)
63
+ * @method $this hget($key, $field)
64
+ * @method $this hgetall($key)
65
+ * @method $this hincrby($key, $field, $increment)
66
+ * @method $this hincrbyfloat($key, $field, $increment)
67
+ * @method $this hkeys($key)
68
+ * @method $this hlen($key)
69
+ * @method $this hmget($key, array $fields)
70
+ * @method $this hmset($key, array $dictionary)
71
+ * @method $this hscan($key, $cursor, array $options = null)
72
+ * @method $this hset($key, $field, $value)
73
+ * @method $this hsetnx($key, $field, $value)
74
+ * @method $this hvals($key)
75
+ * @method $this hstrlen($key, $field)
76
+ * @method $this blpop(array $keys, $timeout)
77
+ * @method $this brpop(array $keys, $timeout)
78
+ * @method $this brpoplpush($source, $destination, $timeout)
79
+ * @method $this lindex($key, $index)
80
+ * @method $this linsert($key, $whence, $pivot, $value)
81
+ * @method $this llen($key)
82
+ * @method $this lpop($key)
83
+ * @method $this lpush($key, array $values)
84
+ * @method $this lpushx($key, $value)
85
+ * @method $this lrange($key, $start, $stop)
86
+ * @method $this lrem($key, $count, $value)
87
+ * @method $this lset($key, $index, $value)
88
+ * @method $this ltrim($key, $start, $stop)
89
+ * @method $this rpop($key)
90
+ * @method $this rpoplpush($source, $destination)
91
+ * @method $this rpush($key, array $values)
92
+ * @method $this rpushx($key, $value)
93
+ * @method $this sadd($key, array $members)
94
+ * @method $this scard($key)
95
+ * @method $this sdiff(array $keys)
96
+ * @method $this sdiffstore($destination, array $keys)
97
+ * @method $this sinter(array $keys)
98
+ * @method $this sinterstore($destination, array $keys)
99
+ * @method $this sismember($key, $member)
100
+ * @method $this smembers($key)
101
+ * @method $this smove($source, $destination, $member)
102
+ * @method $this spop($key, $count = null)
103
+ * @method $this srandmember($key, $count = null)
104
+ * @method $this srem($key, $member)
105
+ * @method $this sscan($key, $cursor, array $options = null)
106
+ * @method $this sunion(array $keys)
107
+ * @method $this sunionstore($destination, array $keys)
108
+ * @method $this zadd($key, array $membersAndScoresDictionary)
109
+ * @method $this zcard($key)
110
+ * @method $this zcount($key, $min, $max)
111
+ * @method $this zincrby($key, $increment, $member)
112
+ * @method $this zinterstore($destination, array $keys, array $options = null)
113
+ * @method $this zrange($key, $start, $stop, array $options = null)
114
+ * @method $this zrangebyscore($key, $min, $max, array $options = null)
115
+ * @method $this zrank($key, $member)
116
+ * @method $this zrem($key, $member)
117
+ * @method $this zremrangebyrank($key, $start, $stop)
118
+ * @method $this zremrangebyscore($key, $min, $max)
119
+ * @method $this zrevrange($key, $start, $stop, array $options = null)
120
+ * @method $this zrevrangebyscore($key, $min, $max, array $options = null)
121
+ * @method $this zrevrank($key, $member)
122
+ * @method $this zunionstore($destination, array $keys, array $options = null)
123
+ * @method $this zscore($key, $member)
124
+ * @method $this zscan($key, $cursor, array $options = null)
125
+ * @method $this zrangebylex($key, $start, $stop, array $options = null)
126
+ * @method $this zrevrangebylex($key, $start, $stop, array $options = null)
127
+ * @method $this zremrangebylex($key, $min, $max)
128
+ * @method $this zlexcount($key, $min, $max)
129
+ * @method $this pfadd($key, array $elements)
130
+ * @method $this pfmerge($destinationKey, array $sourceKeys)
131
+ * @method $this pfcount(array $keys)
132
+ * @method $this pubsub($subcommand, $argument)
133
+ * @method $this publish($channel, $message)
134
+ * @method $this discard()
135
+ * @method $this exec()
136
+ * @method $this multi()
137
+ * @method $this unwatch()
138
+ * @method $this watch($key)
139
+ * @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
140
+ * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
141
+ * @method $this script($subcommand, $argument = null)
142
+ * @method $this auth($password)
143
+ * @method $this echo($message)
144
+ * @method $this ping($message = null)
145
+ * @method $this select($database)
146
+ * @method $this bgrewriteaof()
147
+ * @method $this bgsave()
148
+ * @method $this client($subcommand, $argument = null)
149
+ * @method $this config($subcommand, $argument = null)
150
+ * @method $this dbsize()
151
+ * @method $this flushall()
152
+ * @method $this flushdb()
153
+ * @method $this info($section = null)
154
+ * @method $this lastsave()
155
+ * @method $this save()
156
+ * @method $this slaveof($host, $port)
157
+ * @method $this slowlog($subcommand, $argument = null)
158
+ * @method $this time()
159
+ * @method $this command()
160
+ * @method $this geoadd($key, $longitude, $latitude, $member)
161
+ * @method $this geohash($key, array $members)
162
+ * @method $this geopos($key, array $members)
163
+ * @method $this geodist($key, $member1, $member2, $unit = null)
164
+ * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
165
+ * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
166
+ *
167
+ * @author Daniele Alessandri <suppakilla@gmail.com>
168
+ */
169
+ interface ClientContextInterface
170
+ {
171
+ /**
172
+ * Sends the specified command instance to Redis.
173
+ *
174
+ * @param CommandInterface $command Command instance.
175
+ *
176
+ * @return mixed
177
+ */
178
+ public function executeCommand(CommandInterface $command);
179
+
180
+ /**
181
+ * Sends the specified command with its arguments to Redis.
182
+ *
183
+ * @param string $method Command ID.
184
+ * @param array $arguments Arguments for the command.
185
+ *
186
+ * @return mixed
187
+ */
188
+ public function __call($method, $arguments);
189
+
190
+ /**
191
+ * Starts the execution of the context.
192
+ *
193
+ * @param mixed $callable Optional callback for execution.
194
+ *
195
+ * @return array
196
+ */
197
+ public function execute($callable = null);
198
+ }
includes/predis/src/ClientException.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ /**
15
+ * Exception class that identifies client-side errors.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ClientException extends PredisException
20
+ {
21
+ }
includes/predis/src/ClientInterface.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Configuration\OptionsInterface;
16
+ use Predis\Connection\ConnectionInterface;
17
+ use Predis\Profile\ProfileInterface;
18
+
19
+ /**
20
+ * Interface defining a client able to execute commands against Redis.
21
+ *
22
+ * All the commands exposed by the client generally have the same signature as
23
+ * described by the Redis documentation, but some of them offer an additional
24
+ * and more friendly interface to ease programming which is described in the
25
+ * following list of methods:
26
+ *
27
+ * @method int del(array $keys)
28
+ * @method string dump($key)
29
+ * @method int exists($key)
30
+ * @method int expire($key, $seconds)
31
+ * @method int expireat($key, $timestamp)
32
+ * @method array keys($pattern)
33
+ * @method int move($key, $db)
34
+ * @method mixed object($subcommand, $key)
35
+ * @method int persist($key)
36
+ * @method int pexpire($key, $milliseconds)
37
+ * @method int pexpireat($key, $timestamp)
38
+ * @method int pttl($key)
39
+ * @method string randomkey()
40
+ * @method mixed rename($key, $target)
41
+ * @method int renamenx($key, $target)
42
+ * @method array scan($cursor, array $options = null)
43
+ * @method array sort($key, array $options = null)
44
+ * @method int ttl($key)
45
+ * @method mixed type($key)
46
+ * @method int append($key, $value)
47
+ * @method int bitcount($key, $start = null, $end = null)
48
+ * @method int bitop($operation, $destkey, $key)
49
+ * @method array bitfield($key, $subcommand, ...$subcommandArg)
50
+ * @method int decr($key)
51
+ * @method int decrby($key, $decrement)
52
+ * @method string get($key)
53
+ * @method int getbit($key, $offset)
54
+ * @method string getrange($key, $start, $end)
55
+ * @method string getset($key, $value)
56
+ * @method int incr($key)
57
+ * @method int incrby($key, $increment)
58
+ * @method string incrbyfloat($key, $increment)
59
+ * @method array mget(array $keys)
60
+ * @method mixed mset(array $dictionary)
61
+ * @method int msetnx(array $dictionary)
62
+ * @method mixed psetex($key, $milliseconds, $value)
63
+ * @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
64
+ * @method int setbit($key, $offset, $value)
65
+ * @method int setex($key, $seconds, $value)
66
+ * @method int setnx($key, $value)
67
+ * @method int setrange($key, $offset, $value)
68
+ * @method int strlen($key)
69
+ * @method int hdel($key, array $fields)
70
+ * @method int hexists($key, $field)
71
+ * @method string hget($key, $field)
72
+ * @method array hgetall($key)
73
+ * @method int hincrby($key, $field, $increment)
74
+ * @method string hincrbyfloat($key, $field, $increment)
75
+ * @method array hkeys($key)
76
+ * @method int hlen($key)
77
+ * @method array hmget($key, array $fields)
78
+ * @method mixed hmset($key, array $dictionary)
79
+ * @method array hscan($key, $cursor, array $options = null)
80
+ * @method int hset($key, $field, $value)
81
+ * @method int hsetnx($key, $field, $value)
82
+ * @method array hvals($key)
83
+ * @method int hstrlen($key, $field)
84
+ * @method array blpop(array $keys, $timeout)
85
+ * @method array brpop(array $keys, $timeout)
86
+ * @method array brpoplpush($source, $destination, $timeout)
87
+ * @method string lindex($key, $index)
88
+ * @method int linsert($key, $whence, $pivot, $value)
89
+ * @method int llen($key)
90
+ * @method string lpop($key)
91
+ * @method int lpush($key, array $values)
92
+ * @method int lpushx($key, $value)
93
+ * @method array lrange($key, $start, $stop)
94
+ * @method int lrem($key, $count, $value)
95
+ * @method mixed lset($key, $index, $value)
96
+ * @method mixed ltrim($key, $start, $stop)
97
+ * @method string rpop($key)
98
+ * @method string rpoplpush($source, $destination)
99
+ * @method int rpush($key, array $values)
100
+ * @method int rpushx($key, $value)
101
+ * @method int sadd($key, array $members)
102
+ * @method int scard($key)
103
+ * @method array sdiff(array $keys)
104
+ * @method int sdiffstore($destination, array $keys)
105
+ * @method array sinter(array $keys)
106
+ * @method int sinterstore($destination, array $keys)
107
+ * @method int sismember($key, $member)
108
+ * @method array smembers($key)
109
+ * @method int smove($source, $destination, $member)
110
+ * @method string spop($key, $count = null)
111
+ * @method string srandmember($key, $count = null)
112
+ * @method int srem($key, $member)
113
+ * @method array sscan($key, $cursor, array $options = null)
114
+ * @method array sunion(array $keys)
115
+ * @method int sunionstore($destination, array $keys)
116
+ * @method int zadd($key, array $membersAndScoresDictionary)
117
+ * @method int zcard($key)
118
+ * @method string zcount($key, $min, $max)
119
+ * @method string zincrby($key, $increment, $member)
120
+ * @method int zinterstore($destination, array $keys, array $options = null)
121
+ * @method array zrange($key, $start, $stop, array $options = null)
122
+ * @method array zrangebyscore($key, $min, $max, array $options = null)
123
+ * @method int zrank($key, $member)
124
+ * @method int zrem($key, $member)
125
+ * @method int zremrangebyrank($key, $start, $stop)
126
+ * @method int zremrangebyscore($key, $min, $max)
127
+ * @method array zrevrange($key, $start, $stop, array $options = null)
128
+ * @method array zrevrangebyscore($key, $max, $min, array $options = null)
129
+ * @method int zrevrank($key, $member)
130
+ * @method int zunionstore($destination, array $keys, array $options = null)
131
+ * @method string zscore($key, $member)
132
+ * @method array zscan($key, $cursor, array $options = null)
133
+ * @method array zrangebylex($key, $start, $stop, array $options = null)
134
+ * @method array zrevrangebylex($key, $start, $stop, array $options = null)
135
+ * @method int zremrangebylex($key, $min, $max)
136
+ * @method int zlexcount($key, $min, $max)
137
+ * @method int pfadd($key, array $elements)
138
+ * @method mixed pfmerge($destinationKey, array $sourceKeys)
139
+ * @method int pfcount(array $keys)
140
+ * @method mixed pubsub($subcommand, $argument)
141
+ * @method int publish($channel, $message)
142
+ * @method mixed discard()
143
+ * @method array exec()
144
+ * @method mixed multi()
145
+ * @method mixed unwatch()
146
+ * @method mixed watch($key)
147
+ * @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
148
+ * @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
149
+ * @method mixed script($subcommand, $argument = null)
150
+ * @method mixed auth($password)
151
+ * @method string echo($message)
152
+ * @method mixed ping($message = null)
153
+ * @method mixed select($database)
154
+ * @method mixed bgrewriteaof()
155
+ * @method mixed bgsave()
156
+ * @method mixed client($subcommand, $argument = null)
157
+ * @method mixed config($subcommand, $argument = null)
158
+ * @method int dbsize()
159
+ * @method mixed flushall()
160
+ * @method mixed flushdb()
161
+ * @method array info($section = null)
162
+ * @method int lastsave()
163
+ * @method mixed save()
164
+ * @method mixed slaveof($host, $port)
165
+ * @method mixed slowlog($subcommand, $argument = null)
166
+ * @method array time()
167
+ * @method array command()
168
+ * @method int geoadd($key, $longitude, $latitude, $member)
169
+ * @method array geohash($key, array $members)
170
+ * @method array geopos($key, array $members)
171
+ * @method string geodist($key, $member1, $member2, $unit = null)
172
+ * @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
173
+ * @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
174
+ *
175
+ * @author Daniele Alessandri <suppakilla@gmail.com>
176
+ */
177
+ interface ClientInterface
178
+ {
179
+ /**
180
+ * Returns the server profile used by the client.
181
+ *
182
+ * @return ProfileInterface
183
+ */
184
+ public function getProfile();
185
+
186
+ /**
187
+ * Returns the client options specified upon initialization.
188
+ *
189
+ * @return OptionsInterface
190
+ */
191
+ public function getOptions();
192
+
193
+ /**
194
+ * Opens the underlying connection to the server.
195
+ */
196
+ public function connect();
197
+
198
+ /**
199
+ * Closes the underlying connection from the server.
200
+ */
201
+ public function disconnect();
202
+
203
+ /**
204
+ * Returns the underlying connection instance.
205
+ *
206
+ * @return ConnectionInterface
207
+ */
208
+ public function getConnection();
209
+
210
+ /**
211
+ * Creates a new instance of the specified Redis command.
212
+ *
213
+ * @param string $method Command ID.
214
+ * @param array $arguments Arguments for the command.
215
+ *
216
+ * @return CommandInterface
217
+ */
218
+ public function createCommand($method, $arguments = array());
219
+
220
+ /**
221
+ * Executes the specified Redis command.
222
+ *
223
+ * @param CommandInterface $command Command instance.
224
+ *
225
+ * @return mixed
226
+ */
227
+ public function executeCommand(CommandInterface $command);
228
+
229
+ /**
230
+ * Creates a Redis command with the specified arguments and sends a request
231
+ * to the server.
232
+ *
233
+ * @param string $method Command ID.
234
+ * @param array $arguments Arguments for the command.
235
+ *
236
+ * @return mixed
237
+ */
238
+ public function __call($method, $arguments);
239
+ }
includes/predis/src/Cluster/ClusterStrategy.php ADDED
@@ -0,0 +1,469 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Command\ScriptCommand;
16
+
17
+ /**
18
+ * Common class implementing the logic needed to support clustering strategies.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ abstract class ClusterStrategy implements StrategyInterface
23
+ {
24
+ protected $commands;
25
+
26
+ /**
27
+ *
28
+ */
29
+ public function __construct()
30
+ {
31
+ $this->commands = $this->getDefaultCommands();
32
+ }
33
+
34
+ /**
35
+ * Returns the default map of supported commands with their handlers.
36
+ *
37
+ * @return array
38
+ */
39
+ protected function getDefaultCommands()
40
+ {
41
+ $getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
42
+ $getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
43
+
44
+ return array(
45
+ /* commands operating on the key space */
46
+ 'EXISTS' => $getKeyFromAllArguments,
47
+ 'DEL' => $getKeyFromAllArguments,
48
+ 'TYPE' => $getKeyFromFirstArgument,
49
+ 'EXPIRE' => $getKeyFromFirstArgument,
50
+ 'EXPIREAT' => $getKeyFromFirstArgument,
51
+ 'PERSIST' => $getKeyFromFirstArgument,
52
+ 'PEXPIRE' => $getKeyFromFirstArgument,
53
+ 'PEXPIREAT' => $getKeyFromFirstArgument,
54
+ 'TTL' => $getKeyFromFirstArgument,
55
+ 'PTTL' => $getKeyFromFirstArgument,
56
+ 'SORT' => array($this, 'getKeyFromSortCommand'),
57
+ 'DUMP' => $getKeyFromFirstArgument,
58
+ 'RESTORE' => $getKeyFromFirstArgument,
59
+
60
+ /* commands operating on string values */
61
+ 'APPEND' => $getKeyFromFirstArgument,
62
+ 'DECR' => $getKeyFromFirstArgument,
63
+ 'DECRBY' => $getKeyFromFirstArgument,
64
+ 'GET' => $getKeyFromFirstArgument,
65
+ 'GETBIT' => $getKeyFromFirstArgument,
66
+ 'MGET' => $getKeyFromAllArguments,
67
+ 'SET' => $getKeyFromFirstArgument,
68
+ 'GETRANGE' => $getKeyFromFirstArgument,
69
+ 'GETSET' => $getKeyFromFirstArgument,
70
+ 'INCR' => $getKeyFromFirstArgument,
71
+ 'INCRBY' => $getKeyFromFirstArgument,
72
+ 'INCRBYFLOAT' => $getKeyFromFirstArgument,
73
+ 'SETBIT' => $getKeyFromFirstArgument,
74
+ 'SETEX' => $getKeyFromFirstArgument,
75
+ 'MSET' => array($this, 'getKeyFromInterleavedArguments'),
76
+ 'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
77
+ 'SETNX' => $getKeyFromFirstArgument,
78
+ 'SETRANGE' => $getKeyFromFirstArgument,
79
+ 'STRLEN' => $getKeyFromFirstArgument,
80
+ 'SUBSTR' => $getKeyFromFirstArgument,
81
+ 'BITOP' => array($this, 'getKeyFromBitOp'),
82
+ 'BITCOUNT' => $getKeyFromFirstArgument,
83
+ 'BITFIELD' => $getKeyFromFirstArgument,
84
+
85
+ /* commands operating on lists */
86
+ 'LINSERT' => $getKeyFromFirstArgument,
87
+ 'LINDEX' => $getKeyFromFirstArgument,
88
+ 'LLEN' => $getKeyFromFirstArgument,
89
+ 'LPOP' => $getKeyFromFirstArgument,
90
+ 'RPOP' => $getKeyFromFirstArgument,
91
+ 'RPOPLPUSH' => $getKeyFromAllArguments,
92
+ 'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
93
+ 'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
94
+ 'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
95
+ 'LPUSH' => $getKeyFromFirstArgument,
96
+ 'LPUSHX' => $getKeyFromFirstArgument,
97
+ 'RPUSH' => $getKeyFromFirstArgument,
98
+ 'RPUSHX' => $getKeyFromFirstArgument,
99
+ 'LRANGE' => $getKeyFromFirstArgument,
100
+ 'LREM' => $getKeyFromFirstArgument,
101
+ 'LSET' => $getKeyFromFirstArgument,
102
+ 'LTRIM' => $getKeyFromFirstArgument,
103
+
104
+ /* commands operating on sets */
105
+ 'SADD' => $getKeyFromFirstArgument,
106
+ 'SCARD' => $getKeyFromFirstArgument,
107
+ 'SDIFF' => $getKeyFromAllArguments,
108
+ 'SDIFFSTORE' => $getKeyFromAllArguments,
109
+ 'SINTER' => $getKeyFromAllArguments,
110
+ 'SINTERSTORE' => $getKeyFromAllArguments,
111
+ 'SUNION' => $getKeyFromAllArguments,
112
+ 'SUNIONSTORE' => $getKeyFromAllArguments,
113
+ 'SISMEMBER' => $getKeyFromFirstArgument,
114
+ 'SMEMBERS' => $getKeyFromFirstArgument,
115
+ 'SSCAN' => $getKeyFromFirstArgument,
116
+ 'SPOP' => $getKeyFromFirstArgument,
117
+ 'SRANDMEMBER' => $getKeyFromFirstArgument,
118
+ 'SREM' => $getKeyFromFirstArgument,
119
+
120
+ /* commands operating on sorted sets */
121
+ 'ZADD' => $getKeyFromFirstArgument,
122
+ 'ZCARD' => $getKeyFromFirstArgument,
123
+ 'ZCOUNT' => $getKeyFromFirstArgument,
124
+ 'ZINCRBY' => $getKeyFromFirstArgument,
125
+ 'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
126
+ 'ZRANGE' => $getKeyFromFirstArgument,
127
+ 'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
128
+ 'ZRANK' => $getKeyFromFirstArgument,
129
+ 'ZREM' => $getKeyFromFirstArgument,
130
+ 'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
131
+ 'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
132
+ 'ZREVRANGE' => $getKeyFromFirstArgument,
133
+ 'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
134
+ 'ZREVRANK' => $getKeyFromFirstArgument,
135
+ 'ZSCORE' => $getKeyFromFirstArgument,
136
+ 'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
137
+ 'ZSCAN' => $getKeyFromFirstArgument,
138
+ 'ZLEXCOUNT' => $getKeyFromFirstArgument,
139
+ 'ZRANGEBYLEX' => $getKeyFromFirstArgument,
140
+ 'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
141
+ 'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
142
+
143
+ /* commands operating on hashes */
144
+ 'HDEL' => $getKeyFromFirstArgument,
145
+ 'HEXISTS' => $getKeyFromFirstArgument,
146
+ 'HGET' => $getKeyFromFirstArgument,
147
+ 'HGETALL' => $getKeyFromFirstArgument,
148
+ 'HMGET' => $getKeyFromFirstArgument,
149
+ 'HMSET' => $getKeyFromFirstArgument,
150
+ 'HINCRBY' => $getKeyFromFirstArgument,
151
+ 'HINCRBYFLOAT' => $getKeyFromFirstArgument,
152
+ 'HKEYS' => $getKeyFromFirstArgument,
153
+ 'HLEN' => $getKeyFromFirstArgument,
154
+ 'HSET' => $getKeyFromFirstArgument,
155
+ 'HSETNX' => $getKeyFromFirstArgument,
156
+ 'HVALS' => $getKeyFromFirstArgument,
157
+ 'HSCAN' => $getKeyFromFirstArgument,
158
+ 'HSTRLEN' => $getKeyFromFirstArgument,
159
+
160
+ /* commands operating on HyperLogLog */
161
+ 'PFADD' => $getKeyFromFirstArgument,
162
+ 'PFCOUNT' => $getKeyFromAllArguments,
163
+ 'PFMERGE' => $getKeyFromAllArguments,
164
+
165
+ /* scripting */
166
+ 'EVAL' => array($this, 'getKeyFromScriptingCommands'),
167
+ 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
168
+
169
+ /* commands performing geospatial operations */
170
+ 'GEOADD' => $getKeyFromFirstArgument,
171
+ 'GEOHASH' => $getKeyFromFirstArgument,
172
+ 'GEOPOS' => $getKeyFromFirstArgument,
173
+ 'GEODIST' => $getKeyFromFirstArgument,
174
+ 'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'),
175
+ 'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'),
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Returns the list of IDs for the supported commands.
181
+ *
182
+ * @return array
183
+ */
184
+ public function getSupportedCommands()
185
+ {
186
+ return array_keys($this->commands);
187
+ }
188
+
189
+ /**
190
+ * Sets an handler for the specified command ID.
191
+ *
192
+ * The signature of the callback must have a single parameter of type
193
+ * Predis\Command\CommandInterface.
194
+ *
195
+ * When the callback argument is omitted or NULL, the previously associated
196
+ * handler for the specified command ID is removed.
197
+ *
198
+ * @param string $commandID Command ID.
199
+ * @param mixed $callback A valid callable object, or NULL to unset the handler.
200
+ *
201
+ * @throws \InvalidArgumentException
202
+ */
203
+ public function setCommandHandler($commandID, $callback = null)
204
+ {
205
+ $commandID = strtoupper($commandID);
206
+
207
+ if (!isset($callback)) {
208
+ unset($this->commands[$commandID]);
209
+
210
+ return;
211
+ }
212
+
213
+ if (!is_callable($callback)) {
214
+ throw new \InvalidArgumentException(
215
+ 'The argument must be a callable object or NULL.'
216
+ );
217
+ }
218
+
219
+ $this->commands[$commandID] = $callback;
220
+ }
221
+
222
+ /**
223
+ * Extracts the key from the first argument of a command instance.
224
+ *
225
+ * @param CommandInterface $command Command instance.
226
+ *
227
+ * @return string
228
+ */
229
+ protected function getKeyFromFirstArgument(CommandInterface $command)
230
+ {
231
+ return $command->getArgument(0);
232
+ }
233
+
234
+ /**
235
+ * Extracts the key from a command with multiple keys only when all keys in
236
+ * the arguments array produce the same hash.
237
+ *
238
+ * @param CommandInterface $command Command instance.
239
+ *
240
+ * @return string|null
241
+ */
242
+ protected function getKeyFromAllArguments(CommandInterface $command)
243
+ {
244
+ $arguments = $command->getArguments();
245
+
246
+ if ($this->checkSameSlotForKeys($arguments)) {
247
+ return $arguments[0];
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Extracts the key from a command with multiple keys only when all keys in
253
+ * the arguments array produce the same hash.
254
+ *
255
+ * @param CommandInterface $command Command instance.
256
+ *
257
+ * @return string|null
258
+ */
259
+ protected function getKeyFromInterleavedArguments(CommandInterface $command)
260
+ {
261
+ $arguments = $command->getArguments();
262
+ $keys = array();
263
+
264
+ for ($i = 0; $i < count($arguments); $i += 2) {
265
+ $keys[] = $arguments[$i];
266
+ }
267
+
268
+ if ($this->checkSameSlotForKeys($keys)) {
269
+ return $arguments[0];
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Extracts the key from SORT command.
275
+ *
276
+ * @param CommandInterface $command Command instance.
277
+ *
278
+ * @return string|null
279
+ */
280
+ protected function getKeyFromSortCommand(CommandInterface $command)
281
+ {
282
+ $arguments = $command->getArguments();
283
+ $firstKey = $arguments[0];
284
+
285
+ if (1 === $argc = count($arguments)) {
286
+ return $firstKey;
287
+ }
288
+
289
+ $keys = array($firstKey);
290
+
291
+ for ($i = 1; $i < $argc; ++$i) {
292
+ if (strtoupper($arguments[$i]) === 'STORE') {
293
+ $keys[] = $arguments[++$i];
294
+ }
295
+ }
296
+
297
+ if ($this->checkSameSlotForKeys($keys)) {
298
+ return $firstKey;
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Extracts the key from BLPOP and BRPOP commands.
304
+ *
305
+ * @param CommandInterface $command Command instance.
306
+ *
307
+ * @return string|null
308
+ */
309
+ protected function getKeyFromBlockingListCommands(CommandInterface $command)
310
+ {
311
+ $arguments = $command->getArguments();
312
+
313
+ if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
314
+ return $arguments[0];
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Extracts the key from BITOP command.
320
+ *
321
+ * @param CommandInterface $command Command instance.
322
+ *
323
+ * @return string|null
324
+ */
325
+ protected function getKeyFromBitOp(CommandInterface $command)
326
+ {
327
+ $arguments = $command->getArguments();
328
+
329
+ if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) {
330
+ return $arguments[1];
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
336
+ *
337
+ * @param CommandInterface $command Command instance.
338
+ *
339
+ * @return string|null
340
+ */
341
+ protected function getKeyFromGeoradiusCommands(CommandInterface $command)
342
+ {
343
+ $arguments = $command->getArguments();
344
+ $argc = count($arguments);
345
+ $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
346
+
347
+ if ($argc > $startIndex) {
348
+ $keys = array($arguments[0]);
349
+
350
+ for ($i = $startIndex; $i < $argc; ++$i) {
351
+ $argument = strtoupper($arguments[$i]);
352
+ if ($argument === 'STORE' || $argument === 'STOREDIST') {
353
+ $keys[] = $arguments[++$i];
354
+ }
355
+ }
356
+
357
+ if ($this->checkSameSlotForKeys($keys)) {
358
+ return $arguments[0];
359
+ } else {
360
+ return;
361
+ }
362
+ }
363
+
364
+ return $arguments[0];
365
+ }
366
+
367
+ /**
368
+ * Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
369
+ *
370
+ * @param CommandInterface $command Command instance.
371
+ *
372
+ * @return string|null
373
+ */
374
+ protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
375
+ {
376
+ $arguments = $command->getArguments();
377
+ $keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1]));
378
+
379
+ if ($this->checkSameSlotForKeys($keys)) {
380
+ return $arguments[0];
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Extracts the key from EVAL and EVALSHA commands.
386
+ *
387
+ * @param CommandInterface $command Command instance.
388
+ *
389
+ * @return string|null
390
+ */
391
+ protected function getKeyFromScriptingCommands(CommandInterface $command)
392
+ {
393
+ if ($command instanceof ScriptCommand) {
394
+ $keys = $command->getKeys();
395
+ } else {
396
+ $keys = array_slice($args = $command->getArguments(), 2, $args[1]);
397
+ }
398
+
399
+ if ($keys && $this->checkSameSlotForKeys($keys)) {
400
+ return $keys[0];
401
+ }
402
+ }
403
+
404
+ /**
405
+ * {@inheritdoc}
406
+ */
407
+ public function getSlot(CommandInterface $command)
408
+ {
409
+ $slot = $command->getSlot();
410
+
411
+ if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) {
412
+ $key = call_user_func($this->commands[$cmdID], $command);
413
+
414
+ if (isset($key)) {
415
+ $slot = $this->getSlotByKey($key);
416
+ $command->setSlot($slot);
417
+ }
418
+ }
419
+
420
+ return $slot;
421
+ }
422
+
423
+ /**
424
+ * Checks if the specified array of keys will generate the same hash.
425
+ *
426
+ * @param array $keys Array of keys.
427
+ *
428
+ * @return bool
429
+ */
430
+ protected function checkSameSlotForKeys(array $keys)
431
+ {
432
+ if (!$count = count($keys)) {
433
+ return false;
434
+ }
435
+
436
+ $currentSlot = $this->getSlotByKey($keys[0]);
437
+
438
+ for ($i = 1; $i < $count; ++$i) {
439
+ $nextSlot = $this->getSlotByKey($keys[$i]);
440
+
441
+ if ($currentSlot !== $nextSlot) {
442
+ return false;
443
+ }
444
+
445
+ $currentSlot = $nextSlot;
446
+ }
447
+
448
+ return true;
449
+ }
450
+
451
+ /**
452
+ * Returns only the hashable part of a key (delimited by "{...}"), or the
453
+ * whole key if a key tag is not found in the string.
454
+ *
455
+ * @param string $key A key.
456
+ *
457
+ * @return string
458
+ */
459
+ protected function extractKeyTag($key)
460
+ {
461
+ if (false !== $start = strpos($key, '{')) {
462
+ if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) {
463
+ $key = substr($key, $start, $end - $start);
464
+ }
465
+ }
466
+
467
+ return $key;
468
+ }
469
+ }
includes/predis/src/Cluster/Distributor/DistributorInterface.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster\Distributor;
13
+
14
+ use Predis\Cluster\Hash\HashGeneratorInterface;
15
+
16
+ /**
17
+ * A distributor implements the logic to automatically distribute keys among
18
+ * several nodes for client-side sharding.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ interface DistributorInterface
23
+ {
24
+ /**
25
+ * Adds a node to the distributor with an optional weight.
26
+ *
27
+ * @param mixed $node Node object.
28
+ * @param int $weight Weight for the node.
29
+ */
30
+ public function add($node, $weight = null);
31
+
32
+ /**
33
+ * Removes a node from the distributor.
34
+ *
35
+ * @param mixed $node Node object.
36
+ */
37
+ public function remove($node);
38
+
39
+ /**
40
+ * Returns the corresponding slot of a node from the distributor using the
41
+ * computed hash of a key.
42
+ *
43
+ * @param mixed $hash
44
+ *
45
+ * @return mixed
46
+ */
47
+ public function getSlot($hash);
48
+
49
+ /**
50
+ * Returns a node from the distributor using its assigned slot ID.
51
+ *
52
+ * @param mixed $slot
53
+ *
54
+ * @return mixed|null
55
+ */
56
+ public function getBySlot($slot);
57
+
58
+ /**
59
+ * Returns a node from the distributor using the computed hash of a key.
60
+ *
61
+ * @param mixed $hash
62
+ *
63
+ * @return mixed
64
+ */
65
+ public function getByHash($hash);
66
+
67
+ /**
68
+ * Returns a node from the distributor mapping to the specified value.
69
+ *
70
+ * @param string $value
71
+ *
72
+ * @return mixed
73
+ */
74
+ public function get($value);
75
+
76
+ /**
77
+ * Returns the underlying hash generator instance.
78
+ *
79
+ * @return HashGeneratorInterface
80
+ */
81
+ public function getHashGenerator();
82
+ }
includes/predis/src/Cluster/Distributor/EmptyRingException.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster\Distributor;
13
+
14
+ /**
15
+ * Exception class that identifies empty rings.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class EmptyRingException extends \Exception
20
+ {
21
+ }
includes/predis/src/Cluster/Distributor/HashRing.php ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster\Distributor;
13
+
14
+ use Predis\Cluster\Hash\HashGeneratorInterface;
15
+
16
+ /**
17
+ * This class implements an hashring-based distributor that uses the same
18
+ * algorithm of memcache to distribute keys in a cluster using client-side
19
+ * sharding.
20
+ *
21
+ * @author Daniele Alessandri <suppakilla@gmail.com>
22
+ * @author Lorenzo Castelli <lcastelli@gmail.com>
23
+ */
24
+ class HashRing implements DistributorInterface, HashGeneratorInterface
25
+ {
26
+ const DEFAULT_REPLICAS = 128;
27
+ const DEFAULT_WEIGHT = 100;
28
+
29
+ private $ring;
30
+ private $ringKeys;
31
+ private $ringKeysCount;
32
+ private $replicas;
33
+ private $nodeHashCallback;
34
+ private $nodes = array();
35
+
36
+ /**
37
+ * @param int $replicas Number of replicas in the ring.
38
+ * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
39
+ */
40
+ public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
41
+ {
42
+ $this->replicas = $replicas;
43
+ $this->nodeHashCallback = $nodeHashCallback;
44
+ }
45
+
46
+ /**
47
+ * Adds a node to the ring with an optional weight.
48
+ *
49
+ * @param mixed $node Node object.
50
+ * @param int $weight Weight for the node.
51
+ */
52
+ public function add($node, $weight = null)
53
+ {
54
+ // In case of collisions in the hashes of the nodes, the node added
55
+ // last wins, thus the order in which nodes are added is significant.
56
+ $this->nodes[] = array(
57
+ 'object' => $node,
58
+ 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
59
+ );
60
+
61
+ $this->reset();
62
+ }
63
+
64
+ /**
65
+ * {@inheritdoc}
66
+ */
67
+ public function remove($node)
68
+ {
69
+ // A node is removed by resetting the ring so that it's recreated from
70
+ // scratch, in order to reassign possible hashes with collisions to the
71
+ // right node according to the order in which they were added in the
72
+ // first place.
73
+ for ($i = 0; $i < count($this->nodes); ++$i) {
74
+ if ($this->nodes[$i]['object'] === $node) {
75
+ array_splice($this->nodes, $i, 1);
76
+ $this->reset();
77
+
78
+ break;
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Resets the distributor.
85
+ */
86
+ private function reset()
87
+ {
88
+ unset(
89
+ $this->ring,
90
+ $this->ringKeys,
91
+ $this->ringKeysCount
92
+ );
93
+ }
94
+
95
+ /**
96
+ * Returns the initialization status of the distributor.
97
+ *
98
+ * @return bool
99
+ */
100
+ private function isInitialized()
101
+ {
102
+ return isset($this->ringKeys);
103
+ }
104
+
105
+ /**
106
+ * Calculates the total weight of all the nodes in the distributor.
107
+ *
108
+ * @return int
109
+ */
110
+ private function computeTotalWeight()
111
+ {
112
+ $totalWeight = 0;
113
+
114
+ foreach ($this->nodes as $node) {
115
+ $totalWeight += $node['weight'];
116
+ }
117
+
118
+ return $totalWeight;
119
+ }
120
+
121
+ /**
122
+ * Initializes the distributor.
123
+ */
124
+ private function initialize()
125
+ {
126
+ if ($this->isInitialized()) {
127
+ return;
128
+ }
129
+
130
+ if (!$this->nodes) {
131
+ throw new EmptyRingException('Cannot initialize an empty hashring.');
132
+ }
133
+
134
+ $this->ring = array();
135
+ $totalWeight = $this->computeTotalWeight();
136
+ $nodesCount = count($this->nodes);
137
+
138
+ foreach ($this->nodes as $node) {
139
+ $weightRatio = $node['weight'] / $totalWeight;
140
+ $this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
141
+ }
142
+
143
+ ksort($this->ring, SORT_NUMERIC);
144
+ $this->ringKeys = array_keys($this->ring);
145
+ $this->ringKeysCount = count($this->ringKeys);
146
+ }
147
+
148
+ /**
149
+ * Implements the logic needed to add a node to the hashring.
150
+ *
151
+ * @param array $ring Source hashring.
152
+ * @param mixed $node Node object to be added.
153
+ * @param int $totalNodes Total number of nodes.
154
+ * @param int $replicas Number of replicas in the ring.
155
+ * @param float $weightRatio Weight ratio for the node.
156
+ */
157
+ protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
158
+ {
159
+ $nodeObject = $node['object'];
160
+ $nodeHash = $this->getNodeHash($nodeObject);
161
+ $replicas = (int) round($weightRatio * $totalNodes * $replicas);
162
+
163
+ for ($i = 0; $i < $replicas; ++$i) {
164
+ $key = crc32("$nodeHash:$i");
165
+ $ring[$key] = $nodeObject;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * {@inheritdoc}
171
+ */
172
+ protected function getNodeHash($nodeObject)
173
+ {
174
+ if (!isset($this->nodeHashCallback)) {
175
+ return (string) $nodeObject;
176
+ }
177
+
178
+ return call_user_func($this->nodeHashCallback, $nodeObject);
179
+ }
180
+
181
+ /**
182
+ * {@inheritdoc}
183
+ */
184
+ public function hash($value)
185
+ {
186
+ return crc32($value);
187
+ }
188
+
189
+ /**
190
+ * {@inheritdoc}
191
+ */
192
+ public function getByHash($hash)
193
+ {
194
+ return $this->ring[$this->getSlot($hash)];
195
+ }
196
+
197
+ /**
198
+ * {@inheritdoc}
199
+ */
200
+ public function getBySlot($slot)
201
+ {
202
+ $this->initialize();
203
+
204
+ if (isset($this->ring[$slot])) {
205
+ return $this->ring[$slot];
206
+ }
207
+ }
208
+
209
+ /**
210
+ * {@inheritdoc}
211
+ */
212
+ public function getSlot($hash)
213
+ {
214
+ $this->initialize();
215
+
216
+ $ringKeys = $this->ringKeys;
217
+ $upper = $this->ringKeysCount - 1;
218
+ $lower = 0;
219
+
220
+ while ($lower <= $upper) {
221
+ $index = ($lower + $upper) >> 1;
222
+ $item = $ringKeys[$index];
223
+
224
+ if ($item > $hash) {
225
+ $upper = $index - 1;
226
+ } elseif ($item < $hash) {
227
+ $lower = $index + 1;
228
+ } else {
229
+ return $item;
230
+ }
231
+ }
232
+
233
+ return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)];
234
+ }
235
+
236
+ /**
237
+ * {@inheritdoc}
238
+ */
239
+ public function get($value)
240
+ {
241
+ $hash = $this->hash($value);
242
+ $node = $this->getByHash($hash);
243
+
244
+ return $node;
245
+ }
246
+
247
+ /**
248
+ * Implements a strategy to deal with wrap-around errors during binary searches.
249
+ *
250
+ * @param int $upper
251
+ * @param int $lower
252
+ * @param int $ringKeysCount
253
+ *
254
+ * @return int
255
+ */
256
+ protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
257
+ {
258
+ // Binary search for the last item in ringkeys with a value less or
259
+ // equal to the key. If no such item exists, return the last item.
260
+ return $upper >= 0 ? $upper : $ringKeysCount - 1;
261
+ }
262
+
263
+ /**
264
+ * {@inheritdoc}
265
+ */
266
+ public function getHashGenerator()
267
+ {
268
+ return $this;
269
+ }
270
+ }
includes/predis/src/Cluster/Distributor/KetamaRing.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster\Distributor;
13
+
14
+ /**
15
+ * This class implements an hashring-based distributor that uses the same
16
+ * algorithm of libketama to distribute keys in a cluster using client-side
17
+ * sharding.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ * @author Lorenzo Castelli <lcastelli@gmail.com>
21
+ */
22
+ class KetamaRing extends HashRing
23
+ {
24
+ const DEFAULT_REPLICAS = 160;
25
+
26
+ /**
27
+ * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
28
+ */
29
+ public function __construct($nodeHashCallback = null)
30
+ {
31
+ parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
32
+ }
33
+
34
+ /**
35
+ * {@inheritdoc}
36
+ */
37
+ protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
38
+ {
39
+ $nodeObject = $node['object'];
40
+ $nodeHash = $this->getNodeHash($nodeObject);
41
+ $replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
42
+
43
+ for ($i = 0; $i < $replicas; ++$i) {
44
+ $unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
45
+
46
+ foreach ($unpackedDigest as $key) {
47
+ $ring[$key] = $nodeObject;
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * {@inheritdoc}
54
+ */
55
+ public function hash($value)
56
+ {
57
+ $hash = unpack('V', md5($value, true));
58
+
59
+ return $hash[1];
60
+ }
61
+
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
66
+ {
67
+ // Binary search for the first item in ringkeys with a value greater
68
+ // or equal to the key. If no such item exists, return the first item.
69
+ return $lower < $ringKeysCount ? $lower : 0;
70
+ }
71
+ }
includes/predis/src/Cluster/Hash/CRC16.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster\Hash;
13
+
14
+ /**
15
+ * Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class CRC16 implements HashGeneratorInterface
20
+ {
21
+ private static $CCITT_16 = array(
22
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
23
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
24
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
25
+ 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
26
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
27
+ 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
28
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
29
+ 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
30
+ 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
31
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
32
+ 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
33
+ 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
34
+ 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
35
+ 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
36
+ 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
37
+ 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
38
+ 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
39
+ 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
40
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
41
+ 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
42
+ 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
43
+ 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
44
+ 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
45
+ 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
46
+ 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
47
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
48
+ 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
49
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
50
+ 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
51
+ 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
52
+ 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
53
+ 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
54
+ );
55
+
56
+ /**
57
+ * {@inheritdoc}
58
+ */
59
+ public function hash($value)
60
+ {
61
+ // CRC-CCITT-16 algorithm
62
+ $crc = 0;
63
+ $CCITT_16 = self::$CCITT_16;
64
+ $strlen = strlen($value);
65
+
66
+ for ($i = 0; $i < $strlen; ++$i) {
67
+ $crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
68
+ }
69
+
70
+ return $crc;
71
+ }
72
+ }
includes/predis/src/Cluster/Hash/HashGeneratorInterface.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster\Hash;
13
+
14
+ /**
15
+ * An hash generator implements the logic used to calculate the hash of a key to
16
+ * distribute operations among Redis nodes.
17
+ *
18
+ * @author Daniele Alessandri <suppakilla@gmail.com>
19
+ */
20
+ interface HashGeneratorInterface
21
+ {
22
+ /**
23
+ * Generates an hash from a string to be used for distribution.
24
+ *
25
+ * @param string $value String value.
26
+ *
27
+ * @return int
28
+ */
29
+ public function hash($value);
30
+ }
includes/predis/src/Cluster/PredisStrategy.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster;
13
+
14
+ use Predis\Cluster\Distributor\DistributorInterface;
15
+ use Predis\Cluster\Distributor\HashRing;
16
+
17
+ /**
18
+ * Default cluster strategy used by Predis to handle client-side sharding.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ class PredisStrategy extends ClusterStrategy
23
+ {
24
+ protected $distributor;
25
+
26
+ /**
27
+ * @param DistributorInterface $distributor Optional distributor instance.
28
+ */
29
+ public function __construct(DistributorInterface $distributor = null)
30
+ {
31
+ parent::__construct();
32
+
33
+ $this->distributor = $distributor ?: new HashRing();
34
+ }
35
+
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ public function getSlotByKey($key)
40
+ {
41
+ $key = $this->extractKeyTag($key);
42
+ $hash = $this->distributor->hash($key);
43
+ $slot = $this->distributor->getSlot($hash);
44
+
45
+ return $slot;
46
+ }
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ protected function checkSameSlotForKeys(array $keys)
52
+ {
53
+ if (!$count = count($keys)) {
54
+ return false;
55
+ }
56
+
57
+ $currentKey = $this->extractKeyTag($keys[0]);
58
+
59
+ for ($i = 1; $i < $count; ++$i) {
60
+ $nextKey = $this->extractKeyTag($keys[$i]);
61
+
62
+ if ($currentKey !== $nextKey) {
63
+ return false;
64
+ }
65
+
66
+ $currentKey = $nextKey;
67
+ }
68
+
69
+ return true;
70
+ }
71
+
72
+ /**
73
+ * {@inheritdoc}
74
+ */
75
+ public function getDistributor()
76
+ {
77
+ return $this->distributor;
78
+ }
79
+ }
includes/predis/src/Cluster/RedisStrategy.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster;
13
+
14
+ use Predis\Cluster\Hash\CRC16;
15
+ use Predis\Cluster\Hash\HashGeneratorInterface;
16
+ use Predis\NotSupportedException;
17
+
18
+ /**
19
+ * Default class used by Predis to calculate hashes out of keys of
20
+ * commands supported by redis-cluster.
21
+ *
22
+ * @author Daniele Alessandri <suppakilla@gmail.com>
23
+ */
24
+ class RedisStrategy extends ClusterStrategy
25
+ {
26
+ protected $hashGenerator;
27
+
28
+ /**
29
+ * @param HashGeneratorInterface $hashGenerator Hash generator instance.
30
+ */
31
+ public function __construct(HashGeneratorInterface $hashGenerator = null)
32
+ {
33
+ parent::__construct();
34
+
35
+ $this->hashGenerator = $hashGenerator ?: new CRC16();
36
+ }
37
+
38
+ /**
39
+ * {@inheritdoc}
40
+ */
41
+ public function getSlotByKey($key)
42
+ {
43
+ $key = $this->extractKeyTag($key);
44
+ $slot = $this->hashGenerator->hash($key) & 0x3FFF;
45
+
46
+ return $slot;
47
+ }
48
+
49
+ /**
50
+ * {@inheritdoc}
51
+ */
52
+ public function getDistributor()
53
+ {
54
+ throw new NotSupportedException(
55
+ 'This cluster strategy does not provide an external distributor'
56
+ );
57
+ }
58
+ }
includes/predis/src/Cluster/StrategyInterface.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Cluster;
13
+
14
+ use Predis\Cluster\Distributor\DistributorInterface;
15
+ use Predis\Command\CommandInterface;
16
+
17
+ /**
18
+ * Interface for classes defining the strategy used to calculate an hash out of
19
+ * keys extracted from supported commands.
20
+ *
21
+ * This is mostly useful to support clustering via client-side sharding.
22
+ *
23
+ * @author Daniele Alessandri <suppakilla@gmail.com>
24
+ */
25
+ interface StrategyInterface
26
+ {
27
+ /**
28
+ * Returns a slot for the given command used for clustering distribution or
29
+ * NULL when this is not possible.
30
+ *
31
+ * @param CommandInterface $command Command instance.
32
+ *
33
+ * @return int
34
+ */
35
+ public function getSlot(CommandInterface $command);
36
+
37
+ /**
38
+ * Returns a slot for the given key used for clustering distribution or NULL
39
+ * when this is not possible.
40
+ *
41
+ * @param string $key Key string.
42
+ *
43
+ * @return int
44
+ */
45
+ public function getSlotByKey($key);
46
+
47
+ /**
48
+ * Returns a distributor instance to be used by the cluster.
49
+ *
50
+ * @return DistributorInterface
51
+ */
52
+ public function getDistributor();
53
+ }
includes/predis/src/Collection/Iterator/CursorBasedIterator.php ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Collection\Iterator;
13
+
14
+ use Predis\ClientInterface;
15
+ use Predis\NotSupportedException;
16
+
17
+ /**
18
+ * Provides the base implementation for a fully-rewindable PHP iterator that can
19
+ * incrementally iterate over cursor-based collections stored on Redis using the
20
+ * commands in the `SCAN` family.
21
+ *
22
+ * Given their incremental nature with multiple fetches, these kind of iterators
23
+ * offer limited guarantees about the returned elements because the collection
24
+ * can change several times during the iteration process.
25
+ *
26
+ * @see http://redis.io/commands/scan
27
+ *
28
+ * @author Daniele Alessandri <suppakilla@gmail.com>
29
+ */
30
+ abstract class CursorBasedIterator implements \Iterator
31
+ {
32
+ protected $client;
33
+ protected $match;
34
+ protected $count;
35
+
36
+ protected $valid;
37
+ protected $fetchmore;
38
+ protected $elements;
39
+ protected $cursor;
40
+ protected $position;
41
+ protected $current;
42
+
43
+ /**
44
+ * @param ClientInterface $client Client connected to Redis.
45
+ * @param string $match Pattern to match during the server-side iteration.
46
+ * @param int $count Hint used by Redis to compute the number of results per iteration.
47
+ */
48
+ public function __construct(ClientInterface $client, $match = null, $count = null)
49
+ {
50
+ $this->client = $client;
51
+ $this->match = $match;
52
+ $this->count = $count;
53
+
54
+ $this->reset();
55
+ }
56
+
57
+ /**
58
+ * Ensures that the client supports the specified Redis command required to
59
+ * fetch elements from the server to perform the iteration.
60
+ *
61
+ * @param ClientInterface $client Client connected to Redis.
62
+ * @param string $commandID Command ID.
63
+ *
64
+ * @throws NotSupportedException
65
+ */
66
+ protected function requiredCommand(ClientInterface $client, $commandID)
67
+ {
68
+ if (!$client->getProfile()->supportsCommand($commandID)) {
69
+ throw new NotSupportedException("The current profile does not support '$commandID'.");
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Resets the inner state of the iterator.
75
+ */
76
+ protected function reset()
77
+ {
78
+ $this->valid = true;
79
+ $this->fetchmore = true;
80
+ $this->elements = array();
81
+ $this->cursor = 0;
82
+ $this->position = -1;
83
+ $this->current = null;
84
+ }
85
+
86
+ /**
87
+ * Returns an array of options for the `SCAN` command.
88
+ *
89
+ * @return array
90
+ */
91
+ protected function getScanOptions()
92
+ {
93
+ $options = array();
94
+
95
+ if (strlen($this->match) > 0) {
96
+ $options['MATCH'] = $this->match;
97
+ }
98
+
99
+ if ($this->count > 0) {
100
+ $options['COUNT'] = $this->count;
101
+ }
102
+
103
+ return $options;
104
+ }
105
+
106
+ /**
107
+ * Fetches a new set of elements from the remote collection, effectively
108
+ * advancing the iteration process.
109
+ *
110
+ * @return array
111
+ */
112
+ abstract protected function executeCommand();
113
+
114
+ /**
115
+ * Populates the local buffer of elements fetched from the server during
116
+ * the iteration.
117
+ */
118
+ protected function fetch()
119
+ {
120
+ list($cursor, $elements) = $this->executeCommand();
121
+
122
+ if (!$cursor) {
123
+ $this->fetchmore = false;
124
+ }
125
+
126
+ $this->cursor = $cursor;
127
+ $this->elements = $elements;
128
+ }
129
+
130
+ /**
131
+ * Extracts next values for key() and current().
132
+ */
133
+ protected function extractNext()
134
+ {
135
+ ++$this->position;
136
+ $this->current = array_shift($this->elements);
137
+ }
138
+
139
+ /**
140
+ * {@inheritdoc}
141
+ */
142
+ public function rewind()
143
+ {
144
+ $this->reset();
145
+ $this->next();
146
+ }
147
+
148
+ /**
149
+ * {@inheritdoc}
150
+ */
151
+ public function current()
152
+ {
153
+ return $this->current;
154
+ }
155
+
156
+ /**
157
+ * {@inheritdoc}
158
+ */
159
+ public function key()
160
+ {
161
+ return $this->position;
162
+ }
163
+
164
+ /**
165
+ * {@inheritdoc}
166
+ */
167
+ public function next()
168
+ {
169
+ tryFetch: {
170
+ if (!$this->elements && $this->fetchmore) {
171
+ $this->fetch();
172
+ }
173
+
174
+ if ($this->elements) {
175
+ $this->extractNext();
176
+ } elseif ($this->cursor) {
177
+ goto tryFetch;
178
+ } else {
179
+ $this->valid = false;
180
+ }
181
+ }
182
+ }
183
+
184
+ /**
185
+ * {@inheritdoc}
186
+ */
187
+ public function valid()
188
+ {
189
+ return $this->valid;
190
+ }
191
+ }
includes/predis/src/Collection/Iterator/HashKey.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Collection\Iterator;
13
+
14
+ use Predis\ClientInterface;
15
+
16
+ /**
17
+ * Abstracts the iteration of fields and values of an hash by leveraging the
18
+ * HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ *
22
+ * @link http://redis.io/commands/scan
23
+ */
24
+ class HashKey extends CursorBasedIterator
25
+ {
26
+ protected $key;
27
+
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public function __construct(ClientInterface $client, $key, $match = null, $count = null)
32
+ {
33
+ $this->requiredCommand($client, 'HSCAN');
34
+
35
+ parent::__construct($client, $match, $count);
36
+
37
+ $this->key = $key;
38
+ }
39
+
40
+ /**
41
+ * {@inheritdoc}
42
+ */
43
+ protected function executeCommand()
44
+ {
45
+ return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions());
46
+ }
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ protected function extractNext()
52
+ {
53
+ if ($kv = each($this->elements)) {
54
+ $this->position = $kv[0];
55
+ $this->current = $kv[1];
56
+
57
+ unset($this->elements[$this->position]);
58
+ }
59
+ }
60
+ }
includes/predis/src/Collection/Iterator/Keyspace.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Collection\Iterator;
13
+
14
+ use Predis\ClientInterface;
15
+
16
+ /**
17
+ * Abstracts the iteration of the keyspace on a Redis instance by leveraging the
18
+ * SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ *
22
+ * @link http://redis.io/commands/scan
23
+ */
24
+ class Keyspace extends CursorBasedIterator
25
+ {
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public function __construct(ClientInterface $client, $match = null, $count = null)
30
+ {
31
+ $this->requiredCommand($client, 'SCAN');
32
+
33
+ parent::__construct($client, $match, $count);
34
+ }
35
+
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ protected function executeCommand()
40
+ {
41
+ return $this->client->scan($this->cursor, $this->getScanOptions());
42
+ }
43
+ }
includes/predis/src/Collection/Iterator/ListKey.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Collection\Iterator;
13
+
14
+ use Predis\ClientInterface;
15
+ use Predis\NotSupportedException;
16
+
17
+ /**
18
+ * Abstracts the iteration of items stored in a list by leveraging the LRANGE
19
+ * command wrapped in a fully-rewindable PHP iterator.
20
+ *
21
+ * This iterator tries to emulate the behaviour of cursor-based iterators based
22
+ * on the SCAN-family of commands introduced in Redis <= 2.8, meaning that due
23
+ * to its incremental nature with multiple fetches it can only offer limited
24
+ * guarantees on the returned elements because the collection can change several
25
+ * times (trimmed, deleted, overwritten) during the iteration process.
26
+ *
27
+ * @author Daniele Alessandri <suppakilla@gmail.com>
28
+ *
29
+ * @link http://redis.io/commands/lrange
30
+ */
31
+ class ListKey implements \Iterator
32
+ {
33
+ protected $client;
34
+ protected $count;
35
+ protected $key;
36
+
37
+ protected $valid;
38
+ protected $fetchmore;
39
+ protected $elements;
40
+ protected $position;
41
+ protected $current;
42
+
43
+ /**
44
+ * @param ClientInterface $client Client connected to Redis.
45
+ * @param string $key Redis list key.
46
+ * @param int $count Number of items retrieved on each fetch operation.
47
+ *
48
+ * @throws \InvalidArgumentException
49
+ */
50
+ public function __construct(ClientInterface $client, $key, $count = 10)
51
+ {
52
+ $this->requiredCommand($client, 'LRANGE');
53
+
54
+ if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) {
55
+ throw new \InvalidArgumentException('The $count argument must be a positive integer.');
56
+ }
57
+
58
+ $this->client = $client;
59
+ $this->key = $key;
60
+ $this->count = $count;
61
+
62
+ $this->reset();
63
+ }
64
+
65
+ /**
66
+ * Ensures that the client instance supports the specified Redis command
67
+ * required to fetch elements from the server to perform the iteration.
68
+ *
69
+ * @param ClientInterface $client Client connected to Redis.
70
+ * @param string $commandID Command ID.
71
+ *
72
+ * @throws NotSupportedException
73
+ */
74
+ protected function requiredCommand(ClientInterface $client, $commandID)
75
+ {
76
+ if (!$client->getProfile()->supportsCommand($commandID)) {
77
+ throw new NotSupportedException("The current profile does not support '$commandID'.");
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Resets the inner state of the iterator.
83
+ */
84
+ protected function reset()
85
+ {
86
+ $this->valid = true;
87
+ $this->fetchmore = true;
88
+ $this->elements = array();
89
+ $this->position = -1;
90
+ $this->current = null;
91
+ }
92
+
93
+ /**
94
+ * Fetches a new set of elements from the remote collection, effectively
95
+ * advancing the iteration process.
96
+ *
97
+ * @return array
98
+ */
99
+ protected function executeCommand()
100
+ {
101
+ return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count);
102
+ }
103
+
104
+ /**
105
+ * Populates the local buffer of elements fetched from the server during the
106
+ * iteration.
107
+ */
108
+ protected function fetch()
109
+ {
110
+ $elements = $this->executeCommand();
111
+
112
+ if (count($elements) < $this->count) {
113
+ $this->fetchmore = false;
114
+ }
115
+
116
+ $this->elements = $elements;
117
+ }
118
+
119
+ /**
120
+ * Extracts next values for key() and current().
121
+ */
122
+ protected function extractNext()
123
+ {
124
+ ++$this->position;
125
+ $this->current = array_shift($this->elements);
126
+ }
127
+
128
+ /**
129
+ * {@inheritdoc}
130
+ */
131
+ public function rewind()
132
+ {
133
+ $this->reset();
134
+ $this->next();
135
+ }
136
+
137
+ /**
138
+ * {@inheritdoc}
139
+ */
140
+ public function current()
141
+ {
142
+ return $this->current;
143
+ }
144
+
145
+ /**
146
+ * {@inheritdoc}
147
+ */
148
+ public function key()
149
+ {
150
+ return $this->position;
151
+ }
152
+
153
+ /**
154
+ * {@inheritdoc}
155
+ */
156
+ public function next()
157
+ {
158
+ if (!$this->elements && $this->fetchmore) {
159
+ $this->fetch();
160
+ }
161
+
162
+ if ($this->elements) {
163
+ $this->extractNext();
164
+ } else {
165
+ $this->valid = false;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * {@inheritdoc}
171
+ */
172
+ public function valid()
173
+ {
174
+ return $this->valid;
175
+ }
176
+ }
includes/predis/src/Collection/Iterator/SetKey.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Collection\Iterator;
13
+
14
+ use Predis\ClientInterface;
15
+
16
+ /**
17
+ * Abstracts the iteration of members stored in a set by leveraging the SSCAN
18
+ * command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ *
22
+ * @link http://redis.io/commands/scan
23
+ */
24
+ class SetKey extends CursorBasedIterator
25
+ {
26
+ protected $key;
27
+
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public function __construct(ClientInterface $client, $key, $match = null, $count = null)
32
+ {
33
+ $this->requiredCommand($client, 'SSCAN');
34
+
35
+ parent::__construct($client, $match, $count);
36
+
37
+ $this->key = $key;
38
+ }
39
+
40
+ /**
41
+ * {@inheritdoc}
42
+ */
43
+ protected function executeCommand()
44
+ {
45
+ return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions());
46
+ }
47
+ }
includes/predis/src/Collection/Iterator/SortedSetKey.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Collection\Iterator;
13
+
14
+ use Predis\ClientInterface;
15
+
16
+ /**
17
+ * Abstracts the iteration of members stored in a sorted set by leveraging the
18
+ * ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ *
22
+ * @link http://redis.io/commands/scan
23
+ */
24
+ class SortedSetKey extends CursorBasedIterator
25
+ {
26
+ protected $key;
27
+
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public function __construct(ClientInterface $client, $key, $match = null, $count = null)
32
+ {
33
+ $this->requiredCommand($client, 'ZSCAN');
34
+
35
+ parent::__construct($client, $match, $count);
36
+
37
+ $this->key = $key;
38
+ }
39
+
40
+ /**
41
+ * {@inheritdoc}
42
+ */
43
+ protected function executeCommand()
44
+ {
45
+ return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions());
46
+ }
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ protected function extractNext()
52
+ {
53
+ if ($kv = each($this->elements)) {
54
+ $this->position = $kv[0];
55
+ $this->current = $kv[1];
56
+
57
+ unset($this->elements[$this->position]);
58
+ }
59
+ }
60
+ }
includes/predis/src/Command/Command.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * Base class for Redis commands.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ abstract class Command implements CommandInterface
20
+ {
21
+ private $slot;
22
+ private $arguments = array();
23
+
24
+ /**
25
+ * Returns a filtered array of the arguments.
26
+ *
27
+ * @param array $arguments List of arguments.
28
+ *
29
+ * @return array
30
+ */
31
+ protected function filterArguments(array $arguments)
32
+ {
33
+ return $arguments;
34
+ }
35
+
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ public function setArguments(array $arguments)
40
+ {
41
+ $this->arguments = $this->filterArguments($arguments);
42
+ unset($this->slot);
43
+ }
44
+
45
+ /**
46
+ * {@inheritdoc}
47
+ */
48
+ public function setRawArguments(array $arguments)
49
+ {
50
+ $this->arguments = $arguments;
51
+ unset($this->slot);
52
+ }
53
+
54
+ /**
55
+ * {@inheritdoc}
56
+ */
57
+ public function getArguments()
58
+ {
59
+ return $this->arguments;
60
+ }
61
+
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ public function getArgument($index)
66
+ {
67
+ if (isset($this->arguments[$index])) {
68
+ return $this->arguments[$index];
69
+ }
70
+ }
71
+
72
+ /**
73
+ * {@inheritdoc}
74
+ */
75
+ public function setSlot($slot)
76
+ {
77
+ $this->slot = $slot;
78
+ }
79
+
80
+ /**
81
+ * {@inheritdoc}
82
+ */
83
+ public function getSlot()
84
+ {
85
+ if (isset($this->slot)) {
86
+ return $this->slot;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * {@inheritdoc}
92
+ */
93
+ public function parseResponse($data)
94
+ {
95
+ return $data;
96
+ }
97
+
98
+ /**
99
+ * Normalizes the arguments array passed to a Redis command.
100
+ *
101
+ * @param array $arguments Arguments for a command.
102
+ *
103
+ * @return array
104
+ */
105
+ public static function normalizeArguments(array $arguments)
106
+ {
107
+ if (count($arguments) === 1 && is_array($arguments[0])) {
108
+ return $arguments[0];
109
+ }
110
+
111
+ return $arguments;
112
+ }
113
+
114
+ /**
115
+ * Normalizes the arguments array passed to a variadic Redis command.
116
+ *
117
+ * @param array $arguments Arguments for a command.
118
+ *
119
+ * @return array
120
+ */
121
+ public static function normalizeVariadic(array $arguments)
122
+ {
123
+ if (count($arguments) === 2 && is_array($arguments[1])) {
124
+ return array_merge(array($arguments[0]), $arguments[1]);
125
+ }
126
+
127
+ return $arguments;
128
+ }
129
+ }
includes/predis/src/Command/CommandInterface.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * Defines an abstraction representing a Redis command.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ interface CommandInterface
20
+ {
21
+ /**
22
+ * Returns the ID of the Redis command. By convention, command identifiers
23
+ * must always be uppercase.
24
+ *
25
+ * @return string
26
+ */
27
+ public function getId();
28
+
29
+ /**
30
+ * Assign the specified slot to the command for clustering distribution.
31
+ *
32
+ * @param int $slot Slot ID.
33
+ */
34
+ public function setSlot($slot);
35
+
36
+ /**
37
+ * Returns the assigned slot of the command for clustering distribution.
38
+ *
39
+ * @return int|null
40
+ */
41
+ public function getSlot();
42
+
43
+ /**
44
+ * Sets the arguments for the command.
45
+ *
46
+ * @param array $arguments List of arguments.
47
+ */
48
+ public function setArguments(array $arguments);
49
+
50
+ /**
51
+ * Sets the raw arguments for the command without processing them.
52
+ *
53
+ * @param array $arguments List of arguments.
54
+ */
55
+ public function setRawArguments(array $arguments);
56
+
57
+ /**
58
+ * Gets the arguments of the command.
59
+ *
60
+ * @return array
61
+ */
62
+ public function getArguments();
63
+
64
+ /**
65
+ * Gets the argument of the command at the specified index.
66
+ *
67
+ * @param int $index Index of the desired argument.
68
+ *
69
+ * @return mixed|null
70
+ */
71
+ public function getArgument($index);
72
+
73
+ /**
74
+ * Parses a raw response and returns a PHP object.
75
+ *
76
+ * @param string $data Binary string containing the whole response.
77
+ *
78
+ * @return mixed
79
+ */
80
+ public function parseResponse($data);
81
+ }
includes/predis/src/Command/ConnectionAuth.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/auth
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ConnectionAuth extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'AUTH';
27
+ }
28
+ }
includes/predis/src/Command/ConnectionEcho.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/echo
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ConnectionEcho extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ECHO';
27
+ }
28
+ }
includes/predis/src/Command/ConnectionPing.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/ping
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ConnectionPing extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PING';
27
+ }
28
+ }
includes/predis/src/Command/ConnectionQuit.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/quit
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ConnectionQuit extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'QUIT';
27
+ }
28
+ }
includes/predis/src/Command/ConnectionSelect.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/select
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ConnectionSelect extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SELECT';
27
+ }
28
+ }
includes/predis/src/Command/GeospatialGeoAdd.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/geoadd
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class GeospatialGeoAdd extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GEOADD';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 2 && is_array($arguments[1])) {
35
+ foreach (array_pop($arguments) as $item) {
36
+ $arguments = array_merge($arguments, $item);
37
+ }
38
+ }
39
+
40
+ return $arguments;
41
+ }
42
+ }
includes/predis/src/Command/GeospatialGeoDist.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/geodist
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class GeospatialGeoDist extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GEODIST';
27
+ }
28
+ }
includes/predis/src/Command/GeospatialGeoHash.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/geohash
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class GeospatialGeoHash extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GEOHASH';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 2 && is_array($arguments[1])) {
35
+ $members = array_pop($arguments);
36
+ $arguments = array_merge($arguments, $members);
37
+ }
38
+
39
+ return $arguments;
40
+ }
41
+ }
includes/predis/src/Command/GeospatialGeoPos.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/geopos
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class GeospatialGeoPos extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GEOPOS';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 2 && is_array($arguments[1])) {
35
+ $members = array_pop($arguments);
36
+ $arguments = array_merge($arguments, $members);
37
+ }
38
+
39
+ return $arguments;
40
+ }
41
+ }
includes/predis/src/Command/GeospatialGeoRadius.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/georadius
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class GeospatialGeoRadius extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GEORADIUS';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if ($arguments && is_array(end($arguments))) {
35
+ $options = array_change_key_case(array_pop($arguments), CASE_UPPER);
36
+
37
+ if (isset($options['WITHCOORD']) && $options['WITHCOORD'] == true) {
38
+ $arguments[] = 'WITHCOORD';
39
+ }
40
+
41
+ if (isset($options['WITHDIST']) && $options['WITHDIST'] == true) {
42
+ $arguments[] = 'WITHDIST';
43
+ }
44
+
45
+ if (isset($options['WITHHASH']) && $options['WITHHASH'] == true) {
46
+ $arguments[] = 'WITHHASH';
47
+ }
48
+
49
+ if (isset($options['COUNT'])) {
50
+ $arguments[] = 'COUNT';
51
+ $arguments[] = $options['COUNT'];
52
+ }
53
+
54
+ if (isset($options['SORT'])) {
55
+ $arguments[] = strtoupper($options['SORT']);
56
+ }
57
+
58
+ if (isset($options['STORE'])) {
59
+ $arguments[] = 'STORE';
60
+ $arguments[] = $options['STORE'];
61
+ }
62
+
63
+ if (isset($options['STOREDIST'])) {
64
+ $arguments[] = 'STOREDIST';
65
+ $arguments[] = $options['STOREDIST'];
66
+ }
67
+ }
68
+
69
+ return $arguments;
70
+ }
71
+ }
includes/predis/src/Command/GeospatialGeoRadiusByMember.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/georadiusbymember
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class GeospatialGeoRadiusByMember extends GeospatialGeoRadius
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GEORADIUSBYMEMBER';
27
+ }
28
+ }
includes/predis/src/Command/HashDelete.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hdel
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashDelete extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HDEL';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeVariadic($arguments);
35
+ }
36
+ }
includes/predis/src/Command/HashExists.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hexists
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashExists extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HEXISTS';
27
+ }
28
+ }
includes/predis/src/Command/HashGet.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hget
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashGet extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HGET';
27
+ }
28
+ }
includes/predis/src/Command/HashGetAll.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hgetall
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashGetAll extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HGETALL';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ $result = array();
35
+
36
+ for ($i = 0; $i < count($data); ++$i) {
37
+ $result[$data[$i]] = $data[++$i];
38
+ }
39
+
40
+ return $result;
41
+ }
42
+ }
includes/predis/src/Command/HashGetMultiple.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hmget
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashGetMultiple extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HMGET';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeVariadic($arguments);
35
+ }
36
+ }
includes/predis/src/Command/HashIncrementBy.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hincrby
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashIncrementBy extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HINCRBY';
27
+ }
28
+ }
includes/predis/src/Command/HashIncrementByFloat.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hincrbyfloat
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashIncrementByFloat extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HINCRBYFLOAT';
27
+ }
28
+ }
includes/predis/src/Command/HashKeys.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hkeys
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashKeys extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HKEYS';
27
+ }
28
+ }
includes/predis/src/Command/HashLength.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hlen
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashLength extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HLEN';
27
+ }
28
+ }
includes/predis/src/Command/HashScan.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hscan
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashScan extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HSCAN';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 3 && is_array($arguments[2])) {
35
+ $options = $this->prepareOptions(array_pop($arguments));
36
+ $arguments = array_merge($arguments, $options);
37
+ }
38
+
39
+ return $arguments;
40
+ }
41
+
42
+ /**
43
+ * Returns a list of options and modifiers compatible with Redis.
44
+ *
45
+ * @param array $options List of options.
46
+ *
47
+ * @return array
48
+ */
49
+ protected function prepareOptions($options)
50
+ {
51
+ $options = array_change_key_case($options, CASE_UPPER);
52
+ $normalized = array();
53
+
54
+ if (!empty($options['MATCH'])) {
55
+ $normalized[] = 'MATCH';
56
+ $normalized[] = $options['MATCH'];
57
+ }
58
+
59
+ if (!empty($options['COUNT'])) {
60
+ $normalized[] = 'COUNT';
61
+ $normalized[] = $options['COUNT'];
62
+ }
63
+
64
+ return $normalized;
65
+ }
66
+
67
+ /**
68
+ * {@inheritdoc}
69
+ */
70
+ public function parseResponse($data)
71
+ {
72
+ if (is_array($data)) {
73
+ $fields = $data[1];
74
+ $result = array();
75
+
76
+ for ($i = 0; $i < count($fields); ++$i) {
77
+ $result[$fields[$i]] = $fields[++$i];
78
+ }
79
+
80
+ $data[1] = $result;
81
+ }
82
+
83
+ return $data;
84
+ }
85
+ }
includes/predis/src/Command/HashSet.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hset
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashSet extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HSET';
27
+ }
28
+ }
includes/predis/src/Command/HashSetMultiple.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hmset
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashSetMultiple extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HMSET';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 2 && is_array($arguments[1])) {
35
+ $flattenedKVs = array($arguments[0]);
36
+ $args = $arguments[1];
37
+
38
+ foreach ($args as $k => $v) {
39
+ $flattenedKVs[] = $k;
40
+ $flattenedKVs[] = $v;
41
+ }
42
+
43
+ return $flattenedKVs;
44
+ }
45
+
46
+ return $arguments;
47
+ }
48
+ }
includes/predis/src/Command/HashSetPreserve.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hsetnx
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashSetPreserve extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HSETNX';
27
+ }
28
+ }
includes/predis/src/Command/HashStringLength.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hstrlen
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashStringLength extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HSTRLEN';
27
+ }
28
+ }
includes/predis/src/Command/HashValues.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/hvals
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HashValues extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'HVALS';
27
+ }
28
+ }
includes/predis/src/Command/HyperLogLogAdd.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/pfadd
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HyperLogLogAdd extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PFADD';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeVariadic($arguments);
35
+ }
36
+ }
includes/predis/src/Command/HyperLogLogCount.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/pfcount
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HyperLogLogCount extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PFCOUNT';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeArguments($arguments);
35
+ }
36
+ }
includes/predis/src/Command/HyperLogLogMerge.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/pfmerge
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class HyperLogLogMerge extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PFMERGE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeArguments($arguments);
35
+ }
36
+ }
includes/predis/src/Command/KeyDelete.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/del
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyDelete extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'DEL';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeArguments($arguments);
35
+ }
36
+ }
includes/predis/src/Command/KeyDump.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/dump
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyDump extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'DUMP';
27
+ }
28
+ }
includes/predis/src/Command/KeyExists.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/exists
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyExists extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'EXISTS';
27
+ }
28
+ }
includes/predis/src/Command/KeyExpire.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/expire
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyExpire extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'EXPIRE';
27
+ }
28
+ }
includes/predis/src/Command/KeyExpireAt.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/expireat
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyExpireAt extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'EXPIREAT';
27
+ }
28
+ }
includes/predis/src/Command/KeyKeys.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/keys
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyKeys extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'KEYS';
27
+ }
28
+ }
includes/predis/src/Command/KeyMigrate.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/migrate
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyMigrate extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'MIGRATE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (is_array(end($arguments))) {
35
+ foreach (array_pop($arguments) as $modifier => $value) {
36
+ $modifier = strtoupper($modifier);
37
+
38
+ if ($modifier === 'COPY' && $value == true) {
39
+ $arguments[] = $modifier;
40
+ }
41
+
42
+ if ($modifier === 'REPLACE' && $value == true) {
43
+ $arguments[] = $modifier;
44
+ }
45
+ }
46
+ }
47
+
48
+ return $arguments;
49
+ }
50
+ }
includes/predis/src/Command/KeyMove.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/move
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyMove extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'MOVE';
27
+ }
28
+ }
includes/predis/src/Command/KeyPersist.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/persist
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyPersist extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PERSIST';
27
+ }
28
+ }
includes/predis/src/Command/KeyPreciseExpire.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/pexpire
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyPreciseExpire extends KeyExpire
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PEXPIRE';
27
+ }
28
+ }
includes/predis/src/Command/KeyPreciseExpireAt.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/pexpireat
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyPreciseExpireAt extends KeyExpireAt
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PEXPIREAT';
27
+ }
28
+ }
includes/predis/src/Command/KeyPreciseTimeToLive.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/pttl
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyPreciseTimeToLive extends KeyTimeToLive
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PTTL';
27
+ }
28
+ }
includes/predis/src/Command/KeyRandom.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/randomkey
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyRandom extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RANDOMKEY';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ return $data !== '' ? $data : null;
35
+ }
36
+ }
includes/predis/src/Command/KeyRename.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/rename
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyRename extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RENAME';
27
+ }
28
+ }
includes/predis/src/Command/KeyRenamePreserve.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/renamenx
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyRenamePreserve extends KeyRename
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RENAMENX';
27
+ }
28
+ }
includes/predis/src/Command/KeyRestore.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/restore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyRestore extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RESTORE';
27
+ }
28
+ }
includes/predis/src/Command/KeyScan.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/scan
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyScan extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SCAN';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 2 && is_array($arguments[1])) {
35
+ $options = $this->prepareOptions(array_pop($arguments));
36
+ $arguments = array_merge($arguments, $options);
37
+ }
38
+
39
+ return $arguments;
40
+ }
41
+
42
+ /**
43
+ * Returns a list of options and modifiers compatible with Redis.
44
+ *
45
+ * @param array $options List of options.
46
+ *
47
+ * @return array
48
+ */
49
+ protected function prepareOptions($options)
50
+ {
51
+ $options = array_change_key_case($options, CASE_UPPER);
52
+ $normalized = array();
53
+
54
+ if (!empty($options['MATCH'])) {
55
+ $normalized[] = 'MATCH';
56
+ $normalized[] = $options['MATCH'];
57
+ }
58
+
59
+ if (!empty($options['COUNT'])) {
60
+ $normalized[] = 'COUNT';
61
+ $normalized[] = $options['COUNT'];
62
+ }
63
+
64
+ return $normalized;
65
+ }
66
+ }
includes/predis/src/Command/KeySort.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sort
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeySort extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SORT';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 1) {
35
+ return $arguments;
36
+ }
37
+
38
+ $query = array($arguments[0]);
39
+ $sortParams = array_change_key_case($arguments[1], CASE_UPPER);
40
+
41
+ if (isset($sortParams['BY'])) {
42
+ $query[] = 'BY';
43
+ $query[] = $sortParams['BY'];
44
+ }
45
+
46
+ if (isset($sortParams['GET'])) {
47
+ $getargs = $sortParams['GET'];
48
+
49
+ if (is_array($getargs)) {
50
+ foreach ($getargs as $getarg) {
51
+ $query[] = 'GET';
52
+ $query[] = $getarg;
53
+ }
54
+ } else {
55
+ $query[] = 'GET';
56
+ $query[] = $getargs;
57
+ }
58
+ }
59
+
60
+ if (isset($sortParams['LIMIT']) &&
61
+ is_array($sortParams['LIMIT']) &&
62
+ count($sortParams['LIMIT']) == 2) {
63
+ $query[] = 'LIMIT';
64
+ $query[] = $sortParams['LIMIT'][0];
65
+ $query[] = $sortParams['LIMIT'][1];
66
+ }
67
+
68
+ if (isset($sortParams['SORT'])) {
69
+ $query[] = strtoupper($sortParams['SORT']);
70
+ }
71
+
72
+ if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) {
73
+ $query[] = 'ALPHA';
74
+ }
75
+
76
+ if (isset($sortParams['STORE'])) {
77
+ $query[] = 'STORE';
78
+ $query[] = $sortParams['STORE'];
79
+ }
80
+
81
+ return $query;
82
+ }
83
+ }
includes/predis/src/Command/KeyTimeToLive.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/ttl
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyTimeToLive extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'TTL';
27
+ }
28
+ }
includes/predis/src/Command/KeyType.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/type
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class KeyType extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'TYPE';
27
+ }
28
+ }
includes/predis/src/Command/ListIndex.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lindex
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListIndex extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LINDEX';
27
+ }
28
+ }
includes/predis/src/Command/ListInsert.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/linsert
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListInsert extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LINSERT';
27
+ }
28
+ }
includes/predis/src/Command/ListLength.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/llen
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListLength extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LLEN';
27
+ }
28
+ }
includes/predis/src/Command/ListPopFirst.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lpop
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPopFirst extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LPOP';
27
+ }
28
+ }
includes/predis/src/Command/ListPopFirstBlocking.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/blpop
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPopFirstBlocking extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BLPOP';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 2 && is_array($arguments[0])) {
35
+ list($arguments, $timeout) = $arguments;
36
+ array_push($arguments, $timeout);
37
+ }
38
+
39
+ return $arguments;
40
+ }
41
+ }
includes/predis/src/Command/ListPopLast.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/rpop
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPopLast extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RPOP';
27
+ }
28
+ }
includes/predis/src/Command/ListPopLastBlocking.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/brpop
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPopLastBlocking extends ListPopFirstBlocking
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BRPOP';
27
+ }
28
+ }
includes/predis/src/Command/ListPopLastPushHead.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/rpoplpush
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPopLastPushHead extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RPOPLPUSH';
27
+ }
28
+ }
includes/predis/src/Command/ListPopLastPushHeadBlocking.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/brpoplpush
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPopLastPushHeadBlocking extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BRPOPLPUSH';
27
+ }
28
+ }
includes/predis/src/Command/ListPushHead.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lpush
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPushHead extends ListPushTail
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LPUSH';
27
+ }
28
+ }
includes/predis/src/Command/ListPushHeadX.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lpushx
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPushHeadX extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LPUSHX';
27
+ }
28
+ }
includes/predis/src/Command/ListPushTail.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/rpush
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPushTail extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RPUSH';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeVariadic($arguments);
35
+ }
36
+ }
includes/predis/src/Command/ListPushTailX.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/rpushx
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListPushTailX extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'RPUSHX';
27
+ }
28
+ }
includes/predis/src/Command/ListRange.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lrange
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListRange extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LRANGE';
27
+ }
28
+ }
includes/predis/src/Command/ListRemove.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lrem
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListRemove extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LREM';
27
+ }
28
+ }
includes/predis/src/Command/ListSet.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lset
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListSet extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LSET';
27
+ }
28
+ }
includes/predis/src/Command/ListTrim.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/ltrim
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ListTrim extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LTRIM';
27
+ }
28
+ }
includes/predis/src/Command/PrefixableCommandInterface.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * Defines a command whose keys can be prefixed.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ interface PrefixableCommandInterface extends CommandInterface
20
+ {
21
+ /**
22
+ * Prefixes all the keys found in the arguments of the command.
23
+ *
24
+ * @param string $prefix String used to prefix the keys.
25
+ */
26
+ public function prefixKeys($prefix);
27
+ }
includes/predis/src/Command/Processor/KeyPrefixProcessor.php ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command\Processor;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Command\PrefixableCommandInterface;
16
+
17
+ /**
18
+ * Command processor capable of prefixing keys stored in the arguments of Redis
19
+ * commands supported.
20
+ *
21
+ * @author Daniele Alessandri <suppakilla@gmail.com>
22
+ */
23
+ class KeyPrefixProcessor implements ProcessorInterface
24
+ {
25
+ private $prefix;
26
+ private $commands;
27
+
28
+ /**
29
+ * @param string $prefix Prefix for the keys.
30
+ */
31
+ public function __construct($prefix)
32
+ {
33
+ $this->prefix = $prefix;
34
+ $this->commands = array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+ 'EXISTS' => 'static::all',
37
+ 'DEL' => 'static::all',
38
+ 'TYPE' => 'static::first',
39
+ 'KEYS' => 'static::first',
40
+ 'RENAME' => 'static::all',
41
+ 'RENAMENX' => 'static::all',
42
+ 'EXPIRE' => 'static::first',
43
+ 'EXPIREAT' => 'static::first',
44
+ 'TTL' => 'static::first',
45
+ 'MOVE' => 'static::first',
46
+ 'SORT' => 'static::sort',
47
+ 'DUMP' => 'static::first',
48
+ 'RESTORE' => 'static::first',
49
+ 'SET' => 'static::first',
50
+ 'SETNX' => 'static::first',
51
+ 'MSET' => 'static::interleaved',
52
+ 'MSETNX' => 'static::interleaved',
53
+ 'GET' => 'static::first',
54
+ 'MGET' => 'static::all',
55
+ 'GETSET' => 'static::first',
56
+ 'INCR' => 'static::first',
57
+ 'INCRBY' => 'static::first',
58
+ 'DECR' => 'static::first',
59
+ 'DECRBY' => 'static::first',
60
+ 'RPUSH' => 'static::first',
61
+ 'LPUSH' => 'static::first',
62
+ 'LLEN' => 'static::first',
63
+ 'LRANGE' => 'static::first',
64
+ 'LTRIM' => 'static::first',
65
+ 'LINDEX' => 'static::first',
66
+ 'LSET' => 'static::first',
67
+ 'LREM' => 'static::first',
68
+ 'LPOP' => 'static::first',
69
+ 'RPOP' => 'static::first',
70
+ 'RPOPLPUSH' => 'static::all',
71
+ 'SADD' => 'static::first',
72
+ 'SREM' => 'static::first',
73
+ 'SPOP' => 'static::first',
74
+ 'SMOVE' => 'static::skipLast',
75
+ 'SCARD' => 'static::first',
76
+ 'SISMEMBER' => 'static::first',
77
+ 'SINTER' => 'static::all',
78
+ 'SINTERSTORE' => 'static::all',
79
+ 'SUNION' => 'static::all',
80
+ 'SUNIONSTORE' => 'static::all',
81
+ 'SDIFF' => 'static::all',
82
+ 'SDIFFSTORE' => 'static::all',
83
+ 'SMEMBERS' => 'static::first',
84
+ 'SRANDMEMBER' => 'static::first',
85
+ 'ZADD' => 'static::first',
86
+ 'ZINCRBY' => 'static::first',
87
+ 'ZREM' => 'static::first',
88
+ 'ZRANGE' => 'static::first',
89
+ 'ZREVRANGE' => 'static::first',
90
+ 'ZRANGEBYSCORE' => 'static::first',
91
+ 'ZCARD' => 'static::first',
92
+ 'ZSCORE' => 'static::first',
93
+ 'ZREMRANGEBYSCORE' => 'static::first',
94
+ /* ---------------- Redis 2.0 ---------------- */
95
+ 'SETEX' => 'static::first',
96
+ 'APPEND' => 'static::first',
97
+ 'SUBSTR' => 'static::first',
98
+ 'BLPOP' => 'static::skipLast',
99
+ 'BRPOP' => 'static::skipLast',
100
+ 'ZUNIONSTORE' => 'static::zsetStore',
101
+ 'ZINTERSTORE' => 'static::zsetStore',
102
+ 'ZCOUNT' => 'static::first',
103
+ 'ZRANK' => 'static::first',
104
+ 'ZREVRANK' => 'static::first',
105
+ 'ZREMRANGEBYRANK' => 'static::first',
106
+ 'HSET' => 'static::first',
107
+ 'HSETNX' => 'static::first',
108
+ 'HMSET' => 'static::first',
109
+ 'HINCRBY' => 'static::first',
110
+ 'HGET' => 'static::first',
111
+ 'HMGET' => 'static::first',
112
+ 'HDEL' => 'static::first',
113
+ 'HEXISTS' => 'static::first',
114
+ 'HLEN' => 'static::first',
115
+ 'HKEYS' => 'static::first',
116
+ 'HVALS' => 'static::first',
117
+ 'HGETALL' => 'static::first',
118
+ 'SUBSCRIBE' => 'static::all',
119
+ 'UNSUBSCRIBE' => 'static::all',
120
+ 'PSUBSCRIBE' => 'static::all',
121
+ 'PUNSUBSCRIBE' => 'static::all',
122
+ 'PUBLISH' => 'static::first',
123
+ /* ---------------- Redis 2.2 ---------------- */
124
+ 'PERSIST' => 'static::first',
125
+ 'STRLEN' => 'static::first',
126
+ 'SETRANGE' => 'static::first',
127
+ 'GETRANGE' => 'static::first',
128
+ 'SETBIT' => 'static::first',
129
+ 'GETBIT' => 'static::first',
130
+ 'RPUSHX' => 'static::first',
131
+ 'LPUSHX' => 'static::first',
132
+ 'LINSERT' => 'static::first',
133
+ 'BRPOPLPUSH' => 'static::skipLast',
134
+ 'ZREVRANGEBYSCORE' => 'static::first',
135
+ 'WATCH' => 'static::all',
136
+ /* ---------------- Redis 2.6 ---------------- */
137
+ 'PTTL' => 'static::first',
138
+ 'PEXPIRE' => 'static::first',
139
+ 'PEXPIREAT' => 'static::first',
140
+ 'PSETEX' => 'static::first',
141
+ 'INCRBYFLOAT' => 'static::first',
142
+ 'BITOP' => 'static::skipFirst',
143
+ 'BITCOUNT' => 'static::first',
144
+ 'HINCRBYFLOAT' => 'static::first',
145
+ 'EVAL' => 'static::evalKeys',
146
+ 'EVALSHA' => 'static::evalKeys',
147
+ 'MIGRATE' => 'static::migrate',
148
+ /* ---------------- Redis 2.8 ---------------- */
149
+ 'SSCAN' => 'static::first',
150
+ 'ZSCAN' => 'static::first',
151
+ 'HSCAN' => 'static::first',
152
+ 'PFADD' => 'static::first',
153
+ 'PFCOUNT' => 'static::all',
154
+ 'PFMERGE' => 'static::all',
155
+ 'ZLEXCOUNT' => 'static::first',
156
+ 'ZRANGEBYLEX' => 'static::first',
157
+ 'ZREMRANGEBYLEX' => 'static::first',
158
+ 'ZREVRANGEBYLEX' => 'static::first',
159
+ 'BITPOS' => 'static::first',
160
+ /* ---------------- Redis 3.2 ---------------- */
161
+ 'HSTRLEN' => 'static::first',
162
+ 'BITFIELD' => 'static::first',
163
+ 'GEOADD' => 'static::first',
164
+ 'GEOHASH' => 'static::first',
165
+ 'GEOPOS' => 'static::first',
166
+ 'GEODIST' => 'static::first',
167
+ 'GEORADIUS' => 'static::georadius',
168
+ 'GEORADIUSBYMEMBER' => 'static::georadius',
169
+ );
170
+ }
171
+
172
+ /**
173
+ * Sets a prefix that is applied to all the keys.
174
+ *
175
+ * @param string $prefix Prefix for the keys.
176
+ */
177
+ public function setPrefix($prefix)
178
+ {
179
+ $this->prefix = $prefix;
180
+ }
181
+
182
+ /**
183
+ * Gets the current prefix.
184
+ *
185
+ * @return string
186
+ */
187
+ public function getPrefix()
188
+ {
189
+ return $this->prefix;
190
+ }
191
+
192
+ /**
193
+ * {@inheritdoc}
194
+ */
195
+ public function process(CommandInterface $command)
196
+ {
197
+ if ($command instanceof PrefixableCommandInterface) {
198
+ $command->prefixKeys($this->prefix);
199
+ } elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
200
+ call_user_func($this->commands[$commandID], $command, $this->prefix);
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Sets an handler for the specified command ID.
206
+ *
207
+ * The callback signature must have 2 parameters of the following types:
208
+ *
209
+ * - Predis\Command\CommandInterface (command instance)
210
+ * - String (prefix)
211
+ *
212
+ * When the callback argument is omitted or NULL, the previously
213
+ * associated handler for the specified command ID is removed.
214
+ *
215
+ * @param string $commandID The ID of the command to be handled.
216
+ * @param mixed $callback A valid callable object or NULL.
217
+ *
218
+ * @throws \InvalidArgumentException
219
+ */
220
+ public function setCommandHandler($commandID, $callback = null)
221
+ {
222
+ $commandID = strtoupper($commandID);
223
+
224
+ if (!isset($callback)) {
225
+ unset($this->commands[$commandID]);
226
+
227
+ return;
228
+ }
229
+
230
+ if (!is_callable($callback)) {
231
+ throw new \InvalidArgumentException(
232
+ 'Callback must be a valid callable object or NULL'
233
+ );
234
+ }
235
+
236
+ $this->commands[$commandID] = $callback;
237
+ }
238
+
239
+ /**
240
+ * {@inheritdoc}
241
+ */
242
+ public function __toString()
243
+ {
244
+ return $this->getPrefix();
245
+ }
246
+
247
+ /**
248
+ * Applies the specified prefix only the first argument.
249
+ *
250
+ * @param CommandInterface $command Command instance.
251
+ * @param string $prefix Prefix string.
252
+ */
253
+ public static function first(CommandInterface $command, $prefix)
254
+ {
255
+ if ($arguments = $command->getArguments()) {
256
+ $arguments[0] = "$prefix{$arguments[0]}";
257
+ $command->setRawArguments($arguments);
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Applies the specified prefix to all the arguments.
263
+ *
264
+ * @param CommandInterface $command Command instance.
265
+ * @param string $prefix Prefix string.
266
+ */
267
+ public static function all(CommandInterface $command, $prefix)
268
+ {
269
+ if ($arguments = $command->getArguments()) {
270
+ foreach ($arguments as &$key) {
271
+ $key = "$prefix$key";
272
+ }
273
+
274
+ $command->setRawArguments($arguments);
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Applies the specified prefix only to even arguments in the list.
280
+ *
281
+ * @param CommandInterface $command Command instance.
282
+ * @param string $prefix Prefix string.
283
+ */
284
+ public static function interleaved(CommandInterface $command, $prefix)
285
+ {
286
+ if ($arguments = $command->getArguments()) {
287
+ $length = count($arguments);
288
+
289
+ for ($i = 0; $i < $length; $i += 2) {
290
+ $arguments[$i] = "$prefix{$arguments[$i]}";
291
+ }
292
+
293
+ $command->setRawArguments($arguments);
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Applies the specified prefix to all the arguments but the first one.
299
+ *
300
+ * @param CommandInterface $command Command instance.
301
+ * @param string $prefix Prefix string.
302
+ */
303
+ public static function skipFirst(CommandInterface $command, $prefix)
304
+ {
305
+ if ($arguments = $command->getArguments()) {
306
+ $length = count($arguments);
307
+
308
+ for ($i = 1; $i < $length; ++$i) {
309
+ $arguments[$i] = "$prefix{$arguments[$i]}";
310
+ }
311
+
312
+ $command->setRawArguments($arguments);
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Applies the specified prefix to all the arguments but the last one.
318
+ *
319
+ * @param CommandInterface $command Command instance.
320
+ * @param string $prefix Prefix string.
321
+ */
322
+ public static function skipLast(CommandInterface $command, $prefix)
323
+ {
324
+ if ($arguments = $command->getArguments()) {
325
+ $length = count($arguments);
326
+
327
+ for ($i = 0; $i < $length - 1; ++$i) {
328
+ $arguments[$i] = "$prefix{$arguments[$i]}";
329
+ }
330
+
331
+ $command->setRawArguments($arguments);
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Applies the specified prefix to the keys of a SORT command.
337
+ *
338
+ * @param CommandInterface $command Command instance.
339
+ * @param string $prefix Prefix string.
340
+ */
341
+ public static function sort(CommandInterface $command, $prefix)
342
+ {
343
+ if ($arguments = $command->getArguments()) {
344
+ $arguments[0] = "$prefix{$arguments[0]}";
345
+
346
+ if (($count = count($arguments)) > 1) {
347
+ for ($i = 1; $i < $count; ++$i) {
348
+ switch (strtoupper($arguments[$i])) {
349
+ case 'BY':
350
+ case 'STORE':
351
+ $arguments[$i] = "$prefix{$arguments[++$i]}";
352
+ break;
353
+
354
+ case 'GET':
355
+ $value = $arguments[++$i];
356
+ if ($value !== '#') {
357
+ $arguments[$i] = "$prefix$value";
358
+ }
359
+ break;
360
+
361
+ case 'LIMIT';
362
+ $i += 2;
363
+ break;
364
+ }
365
+ }
366
+ }
367
+
368
+ $command->setRawArguments($arguments);
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Applies the specified prefix to the keys of an EVAL-based command.
374
+ *
375
+ * @param CommandInterface $command Command instance.
376
+ * @param string $prefix Prefix string.
377
+ */
378
+ public static function evalKeys(CommandInterface $command, $prefix)
379
+ {
380
+ if ($arguments = $command->getArguments()) {
381
+ for ($i = 2; $i < $arguments[1] + 2; ++$i) {
382
+ $arguments[$i] = "$prefix{$arguments[$i]}";
383
+ }
384
+
385
+ $command->setRawArguments($arguments);
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Applies the specified prefix to the keys of Z[INTERSECTION|UNION]STORE.
391
+ *
392
+ * @param CommandInterface $command Command instance.
393
+ * @param string $prefix Prefix string.
394
+ */
395
+ public static function zsetStore(CommandInterface $command, $prefix)
396
+ {
397
+ if ($arguments = $command->getArguments()) {
398
+ $arguments[0] = "$prefix{$arguments[0]}";
399
+ $length = ((int) $arguments[1]) + 2;
400
+
401
+ for ($i = 2; $i < $length; ++$i) {
402
+ $arguments[$i] = "$prefix{$arguments[$i]}";
403
+ }
404
+
405
+ $command->setRawArguments($arguments);
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Applies the specified prefix to the key of a MIGRATE command.
411
+ *
412
+ * @param CommandInterface $command Command instance.
413
+ * @param string $prefix Prefix string.
414
+ */
415
+ public static function migrate(CommandInterface $command, $prefix)
416
+ {
417
+ if ($arguments = $command->getArguments()) {
418
+ $arguments[2] = "$prefix{$arguments[2]}";
419
+ $command->setRawArguments($arguments);
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Applies the specified prefix to the key of a GEORADIUS command.
425
+ *
426
+ * @param CommandInterface $command Command instance.
427
+ * @param string $prefix Prefix string.
428
+ */
429
+ public static function georadius(CommandInterface $command, $prefix)
430
+ {
431
+ if ($arguments = $command->getArguments()) {
432
+ $arguments[0] = "$prefix{$arguments[0]}";
433
+ $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
434
+
435
+ if (($count = count($arguments)) > $startIndex) {
436
+ for ($i = $startIndex; $i < $count; ++$i) {
437
+ switch (strtoupper($arguments[$i])) {
438
+ case 'STORE':
439
+ case 'STOREDIST':
440
+ $arguments[$i] = "$prefix{$arguments[++$i]}";
441
+ break;
442
+
443
+ }
444
+ }
445
+ }
446
+
447
+ $command->setRawArguments($arguments);
448
+ }
449
+ }
450
+ }
includes/predis/src/Command/Processor/ProcessorChain.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command\Processor;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * Default implementation of a command processors chain.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ class ProcessorChain implements \ArrayAccess, ProcessorInterface
22
+ {
23
+ private $processors = array();
24
+
25
+ /**
26
+ * @param array $processors List of instances of ProcessorInterface.
27
+ */
28
+ public function __construct($processors = array())
29
+ {
30
+ foreach ($processors as $processor) {
31
+ $this->add($processor);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * {@inheritdoc}
37
+ */
38
+ public function add(ProcessorInterface $processor)
39
+ {
40
+ $this->processors[] = $processor;
41
+ }
42
+
43
+ /**
44
+ * {@inheritdoc}
45
+ */
46
+ public function remove(ProcessorInterface $processor)
47
+ {
48
+ if (false !== $index = array_search($processor, $this->processors, true)) {
49
+ unset($this[$index]);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * {@inheritdoc}
55
+ */
56
+ public function process(CommandInterface $command)
57
+ {
58
+ for ($i = 0; $i < $count = count($this->processors); ++$i) {
59
+ $this->processors[$i]->process($command);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * {@inheritdoc}
65
+ */
66
+ public function getProcessors()
67
+ {
68
+ return $this->processors;
69
+ }
70
+
71
+ /**
72
+ * Returns an iterator over the list of command processor in the chain.
73
+ *
74
+ * @return \ArrayIterator
75
+ */
76
+ public function getIterator()
77
+ {
78
+ return new \ArrayIterator($this->processors);
79
+ }
80
+
81
+ /**
82
+ * Returns the number of command processors in the chain.
83
+ *
84
+ * @return int
85
+ */
86
+ public function count()
87
+ {
88
+ return count($this->processors);
89
+ }
90
+
91
+ /**
92
+ * {@inheritdoc}
93
+ */
94
+ public function offsetExists($index)
95
+ {
96
+ return isset($this->processors[$index]);
97
+ }
98
+
99
+ /**
100
+ * {@inheritdoc}
101
+ */
102
+ public function offsetGet($index)
103
+ {
104
+ return $this->processors[$index];
105
+ }
106
+
107
+ /**
108
+ * {@inheritdoc}
109
+ */
110
+ public function offsetSet($index, $processor)
111
+ {
112
+ if (!$processor instanceof ProcessorInterface) {
113
+ throw new \InvalidArgumentException(
114
+ 'A processor chain accepts only instances of '.
115
+ "'Predis\Command\Processor\ProcessorInterface'."
116
+ );
117
+ }
118
+
119
+ $this->processors[$index] = $processor;
120
+ }
121
+
122
+ /**
123
+ * {@inheritdoc}
124
+ */
125
+ public function offsetUnset($index)
126
+ {
127
+ unset($this->processors[$index]);
128
+ $this->processors = array_values($this->processors);
129
+ }
130
+ }
includes/predis/src/Command/Processor/ProcessorInterface.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command\Processor;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * A command processor processes Redis commands before they are sent to Redis.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ interface ProcessorInterface
22
+ {
23
+ /**
24
+ * Processes the given Redis command.
25
+ *
26
+ * @param CommandInterface $command Command instance.
27
+ */
28
+ public function process(CommandInterface $command);
29
+ }
includes/predis/src/Command/PubSubPublish.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/publish
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class PubSubPublish extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PUBLISH';
27
+ }
28
+ }
includes/predis/src/Command/PubSubPubsub.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/pubsub
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class PubSubPubsub extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PUBSUB';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ switch (strtolower($this->getArgument(0))) {
35
+ case 'numsub':
36
+ return self::processNumsub($data);
37
+
38
+ default:
39
+ return $data;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Returns the processed response to PUBSUB NUMSUB.
45
+ *
46
+ * @param array $channels List of channels
47
+ *
48
+ * @return array
49
+ */
50
+ protected static function processNumsub(array $channels)
51
+ {
52
+ $processed = array();
53
+ $count = count($channels);
54
+
55
+ for ($i = 0; $i < $count; ++$i) {
56
+ $processed[$channels[$i]] = $channels[++$i];
57
+ }
58
+
59
+ return $processed;
60
+ }
61
+ }
includes/predis/src/Command/PubSubSubscribe.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/subscribe
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class PubSubSubscribe extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SUBSCRIBE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeArguments($arguments);
35
+ }
36
+ }
includes/predis/src/Command/PubSubSubscribeByPattern.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/psubscribe
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class PubSubSubscribeByPattern extends PubSubSubscribe
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PSUBSCRIBE';
27
+ }
28
+ }
includes/predis/src/Command/PubSubUnsubscribe.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/unsubscribe
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class PubSubUnsubscribe extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'UNSUBSCRIBE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeArguments($arguments);
35
+ }
36
+ }
includes/predis/src/Command/PubSubUnsubscribeByPattern.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/punsubscribe
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class PubSubUnsubscribeByPattern extends PubSubUnsubscribe
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PUNSUBSCRIBE';
27
+ }
28
+ }
includes/predis/src/Command/RawCommand.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * Class for generic "anonymous" Redis commands.
16
+ *
17
+ * This command class does not filter input arguments or parse responses, but
18
+ * can be used to leverage the standard Predis API to execute any command simply
19
+ * by providing the needed arguments following the command signature as defined
20
+ * by Redis in its documentation.
21
+ *
22
+ * @author Daniele Alessandri <suppakilla@gmail.com>
23
+ */
24
+ class RawCommand implements CommandInterface
25
+ {
26
+ private $slot;
27
+ private $commandID;
28
+ private $arguments;
29
+
30
+ /**
31
+ * @param array $arguments Command ID and its arguments.
32
+ *
33
+ * @throws \InvalidArgumentException
34
+ */
35
+ public function __construct(array $arguments)
36
+ {
37
+ if (!$arguments) {
38
+ throw new \InvalidArgumentException(
39
+ 'The arguments array must contain at least the command ID.'
40
+ );
41
+ }
42
+
43
+ $this->commandID = strtoupper(array_shift($arguments));
44
+ $this->arguments = $arguments;
45
+ }
46
+
47
+ /**
48
+ * Creates a new raw command using a variadic method.
49
+ *
50
+ * @param string $commandID Redis command ID.
51
+ * @param string ... Arguments list for the command.
52
+ *
53
+ * @return CommandInterface
54
+ */
55
+ public static function create($commandID /* [ $arg, ... */)
56
+ {
57
+ $arguments = func_get_args();
58
+ $command = new self($arguments);
59
+
60
+ return $command;
61
+ }
62
+
63
+ /**
64
+ * {@inheritdoc}
65
+ */
66
+ public function getId()
67
+ {
68
+ return $this->commandID;
69
+ }
70
+
71
+ /**
72
+ * {@inheritdoc}
73
+ */
74
+ public function setArguments(array $arguments)
75
+ {
76
+ $this->arguments = $arguments;
77
+ unset($this->slot);
78
+ }
79
+
80
+ /**
81
+ * {@inheritdoc}
82
+ */
83
+ public function setRawArguments(array $arguments)
84
+ {
85
+ $this->setArguments($arguments);
86
+ }
87
+
88
+ /**
89
+ * {@inheritdoc}
90
+ */
91
+ public function getArguments()
92
+ {
93
+ return $this->arguments;
94
+ }
95
+
96
+ /**
97
+ * {@inheritdoc}
98
+ */
99
+ public function getArgument($index)
100
+ {
101
+ if (isset($this->arguments[$index])) {
102
+ return $this->arguments[$index];
103
+ }
104
+ }
105
+
106
+ /**
107
+ * {@inheritdoc}
108
+ */
109
+ public function setSlot($slot)
110
+ {
111
+ $this->slot = $slot;
112
+ }
113
+
114
+ /**
115
+ * {@inheritdoc}
116
+ */
117
+ public function getSlot()
118
+ {
119
+ if (isset($this->slot)) {
120
+ return $this->slot;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * {@inheritdoc}
126
+ */
127
+ public function parseResponse($data)
128
+ {
129
+ return $data;
130
+ }
131
+ }
includes/predis/src/Command/ScriptCommand.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * Base class used to implement an higher level abstraction for commands based
16
+ * on Lua scripting with EVAL and EVALSHA.
17
+ *
18
+ * @link http://redis.io/commands/eval
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ abstract class ScriptCommand extends ServerEvalSHA
23
+ {
24
+ /**
25
+ * Gets the body of a Lua script.
26
+ *
27
+ * @return string
28
+ */
29
+ abstract public function getScript();
30
+
31
+ /**
32
+ * Specifies the number of arguments that should be considered as keys.
33
+ *
34
+ * The default behaviour for the base class is to return 0 to indicate that
35
+ * all the elements of the arguments array should be considered as keys, but
36
+ * subclasses can enforce a static number of keys.
37
+ *
38
+ * @return int
39
+ */
40
+ protected function getKeysCount()
41
+ {
42
+ return 0;
43
+ }
44
+
45
+ /**
46
+ * Returns the elements from the arguments that are identified as keys.
47
+ *
48
+ * @return array
49
+ */
50
+ public function getKeys()
51
+ {
52
+ return array_slice($this->getArguments(), 2, $this->getKeysCount());
53
+ }
54
+
55
+ /**
56
+ * {@inheritdoc}
57
+ */
58
+ protected function filterArguments(array $arguments)
59
+ {
60
+ if (($numkeys = $this->getKeysCount()) && $numkeys < 0) {
61
+ $numkeys = count($arguments) + $numkeys;
62
+ }
63
+
64
+ return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments);
65
+ }
66
+
67
+ /**
68
+ * @return array
69
+ */
70
+ public function getEvalArguments()
71
+ {
72
+ $arguments = $this->getArguments();
73
+ $arguments[0] = $this->getScript();
74
+
75
+ return $arguments;
76
+ }
77
+ }
includes/predis/src/Command/ServerBackgroundRewriteAOF.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/bgrewriteaof
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerBackgroundRewriteAOF extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BGREWRITEAOF';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ return $data == 'Background append only file rewriting started';
35
+ }
36
+ }
includes/predis/src/Command/ServerBackgroundSave.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/bgsave
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerBackgroundSave extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BGSAVE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ return $data === 'Background saving started' ? true : $data;
35
+ }
36
+ }
includes/predis/src/Command/ServerClient.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/client-list
16
+ * @link http://redis.io/commands/client-kill
17
+ * @link http://redis.io/commands/client-getname
18
+ * @link http://redis.io/commands/client-setname
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ class ServerClient extends Command
23
+ {
24
+ /**
25
+ * {@inheritdoc}
26
+ */
27
+ public function getId()
28
+ {
29
+ return 'CLIENT';
30
+ }
31
+
32
+ /**
33
+ * {@inheritdoc}
34
+ */
35
+ public function parseResponse($data)
36
+ {
37
+ $args = array_change_key_case($this->getArguments(), CASE_UPPER);
38
+
39
+ switch (strtoupper($args[0])) {
40
+ case 'LIST':
41
+ return $this->parseClientList($data);
42
+ case 'KILL':
43
+ case 'GETNAME':
44
+ case 'SETNAME':
45
+ default:
46
+ return $data;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Parses the response to CLIENT LIST and returns a structured list.
52
+ *
53
+ * @param string $data Response buffer.
54
+ *
55
+ * @return array
56
+ */
57
+ protected function parseClientList($data)
58
+ {
59
+ $clients = array();
60
+
61
+ foreach (explode("\n", $data, -1) as $clientData) {
62
+ $client = array();
63
+
64
+ foreach (explode(' ', $clientData) as $kv) {
65
+ @list($k, $v) = explode('=', $kv);
66
+ $client[$k] = $v;
67
+ }
68
+
69
+ $clients[] = $client;
70
+ }
71
+
72
+ return $clients;
73
+ }
74
+ }
includes/predis/src/Command/ServerCommand.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/command
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerCommand extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'COMMAND';
27
+ }
28
+ }
includes/predis/src/Command/ServerConfig.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/config-set
16
+ * @link http://redis.io/commands/config-get
17
+ * @link http://redis.io/commands/config-resetstat
18
+ * @link http://redis.io/commands/config-rewrite
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ class ServerConfig extends Command
23
+ {
24
+ /**
25
+ * {@inheritdoc}
26
+ */
27
+ public function getId()
28
+ {
29
+ return 'CONFIG';
30
+ }
31
+
32
+ /**
33
+ * {@inheritdoc}
34
+ */
35
+ public function parseResponse($data)
36
+ {
37
+ if (is_array($data)) {
38
+ $result = array();
39
+
40
+ for ($i = 0; $i < count($data); ++$i) {
41
+ $result[$data[$i]] = $data[++$i];
42
+ }
43
+
44
+ return $result;
45
+ }
46
+
47
+ return $data;
48
+ }
49
+ }
includes/predis/src/Command/ServerDatabaseSize.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/dbsize
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerDatabaseSize extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'DBSIZE';
27
+ }
28
+ }
includes/predis/src/Command/ServerEval.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/eval
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerEval extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'EVAL';
27
+ }
28
+
29
+ /**
30
+ * Calculates the SHA1 hash of the body of the script.
31
+ *
32
+ * @return string SHA1 hash.
33
+ */
34
+ public function getScriptHash()
35
+ {
36
+ return sha1($this->getArgument(0));
37
+ }
38
+ }
includes/predis/src/Command/ServerEvalSHA.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/evalsha
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerEvalSHA extends ServerEval
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'EVALSHA';
27
+ }
28
+
29
+ /**
30
+ * Returns the SHA1 hash of the body of the script.
31
+ *
32
+ * @return string SHA1 hash.
33
+ */
34
+ public function getScriptHash()
35
+ {
36
+ return $this->getArgument(0);
37
+ }
38
+ }
includes/predis/src/Command/ServerFlushAll.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/flushall
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerFlushAll extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'FLUSHALL';
27
+ }
28
+ }
includes/predis/src/Command/ServerFlushDatabase.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/flushdb
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerFlushDatabase extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'FLUSHDB';
27
+ }
28
+ }
includes/predis/src/Command/ServerInfo.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/info
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerInfo extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'INFO';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ $info = array();
35
+ $infoLines = preg_split('/\r?\n/', $data);
36
+
37
+ foreach ($infoLines as $row) {
38
+ if (strpos($row, ':') === false) {
39
+ continue;
40
+ }
41
+
42
+ list($k, $v) = $this->parseRow($row);
43
+ $info[$k] = $v;
44
+ }
45
+
46
+ return $info;
47
+ }
48
+
49
+ /**
50
+ * Parses a single row of the response and returns the key-value pair.
51
+ *
52
+ * @param string $row Single row of the response.
53
+ *
54
+ * @return array
55
+ */
56
+ protected function parseRow($row)
57
+ {
58
+ list($k, $v) = explode(':', $row, 2);
59
+
60
+ if (preg_match('/^db\d+$/', $k)) {
61
+ $v = $this->parseDatabaseStats($v);
62
+ }
63
+
64
+ return array($k, $v);
65
+ }
66
+
67
+ /**
68
+ * Extracts the statistics of each logical DB from the string buffer.
69
+ *
70
+ * @param string $str Response buffer.
71
+ *
72
+ * @return array
73
+ */
74
+ protected function parseDatabaseStats($str)
75
+ {
76
+ $db = array();
77
+
78
+ foreach (explode(',', $str) as $dbvar) {
79
+ list($dbvk, $dbvv) = explode('=', $dbvar);
80
+ $db[trim($dbvk)] = $dbvv;
81
+ }
82
+
83
+ return $db;
84
+ }
85
+
86
+ /**
87
+ * Parses the response and extracts the allocation statistics.
88
+ *
89
+ * @param string $str Response buffer.
90
+ *
91
+ * @return array
92
+ */
93
+ protected function parseAllocationStats($str)
94
+ {
95
+ $stats = array();
96
+
97
+ foreach (explode(',', $str) as $kv) {
98
+ @list($size, $objects, $extra) = explode('=', $kv);
99
+
100
+ // hack to prevent incorrect values when parsing the >=256 key
101
+ if (isset($extra)) {
102
+ $size = ">=$objects";
103
+ $objects = $extra;
104
+ }
105
+
106
+ $stats[$size] = $objects;
107
+ }
108
+
109
+ return $stats;
110
+ }
111
+ }
includes/predis/src/Command/ServerInfoV26x.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/info
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerInfoV26x extends ServerInfo
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function parseResponse($data)
25
+ {
26
+ if ($data === '') {
27
+ return array();
28
+ }
29
+
30
+ $info = array();
31
+
32
+ $current = null;
33
+ $infoLines = preg_split('/\r?\n/', $data);
34
+
35
+ if (isset($infoLines[0]) && $infoLines[0][0] !== '#') {
36
+ return parent::parseResponse($data);
37
+ }
38
+
39
+ foreach ($infoLines as $row) {
40
+ if ($row === '') {
41
+ continue;
42
+ }
43
+
44
+ if (preg_match('/^# (\w+)$/', $row, $matches)) {
45
+ $info[$matches[1]] = array();
46
+ $current = &$info[$matches[1]];
47
+ continue;
48
+ }
49
+
50
+ list($k, $v) = $this->parseRow($row);
51
+ $current[$k] = $v;
52
+ }
53
+
54
+ return $info;
55
+ }
56
+ }
includes/predis/src/Command/ServerLastSave.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/lastsave
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerLastSave extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'LASTSAVE';
27
+ }
28
+ }
includes/predis/src/Command/ServerMonitor.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/monitor
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerMonitor extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'MONITOR';
27
+ }
28
+ }
includes/predis/src/Command/ServerObject.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/object
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerObject extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'OBJECT';
27
+ }
28
+ }
includes/predis/src/Command/ServerSave.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/save
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerSave extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SAVE';
27
+ }
28
+ }
includes/predis/src/Command/ServerScript.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/script
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerScript extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SCRIPT';
27
+ }
28
+ }
includes/predis/src/Command/ServerSentinel.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/topics/sentinel
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerSentinel extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SENTINEL';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ switch (strtolower($this->getArgument(0))) {
35
+ case 'masters':
36
+ case 'slaves':
37
+ return self::processMastersOrSlaves($data);
38
+
39
+ default:
40
+ return $data;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Returns a processed response to SENTINEL MASTERS or SENTINEL SLAVES.
46
+ *
47
+ * @param array $servers List of Redis servers.
48
+ *
49
+ * @return array
50
+ */
51
+ protected static function processMastersOrSlaves(array $servers)
52
+ {
53
+ foreach ($servers as $idx => $node) {
54
+ $processed = array();
55
+ $count = count($node);
56
+
57
+ for ($i = 0; $i < $count; ++$i) {
58
+ $processed[$node[$i]] = $node[++$i];
59
+ }
60
+
61
+ $servers[$idx] = $processed;
62
+ }
63
+
64
+ return $servers;
65
+ }
66
+ }
includes/predis/src/Command/ServerShutdown.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/shutdown
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerShutdown extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SHUTDOWN';
27
+ }
28
+ }
includes/predis/src/Command/ServerSlaveOf.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/slaveof
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerSlaveOf extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SLAVEOF';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 0 || $arguments[0] === 'NO ONE') {
35
+ return array('NO', 'ONE');
36
+ }
37
+
38
+ return $arguments;
39
+ }
40
+ }
includes/predis/src/Command/ServerSlowlog.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/slowlog
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerSlowlog extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SLOWLOG';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function parseResponse($data)
33
+ {
34
+ if (is_array($data)) {
35
+ $log = array();
36
+
37
+ foreach ($data as $index => $entry) {
38
+ $log[$index] = array(
39
+ 'id' => $entry[0],
40
+ 'timestamp' => $entry[1],
41
+ 'duration' => $entry[2],
42
+ 'command' => $entry[3],
43
+ );
44
+ }
45
+
46
+ return $log;
47
+ }
48
+
49
+ return $data;
50
+ }
51
+ }
includes/predis/src/Command/ServerTime.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/time
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ServerTime extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'TIME';
27
+ }
28
+ }
includes/predis/src/Command/SetAdd.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sadd
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetAdd extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SADD';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeVariadic($arguments);
35
+ }
36
+ }
includes/predis/src/Command/SetCardinality.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/scard
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetCardinality extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SCARD';
27
+ }
28
+ }
includes/predis/src/Command/SetDifference.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sdiff
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetDifference extends SetIntersection
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SDIFF';
27
+ }
28
+ }
includes/predis/src/Command/SetDifferenceStore.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sdiffstore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetDifferenceStore extends SetIntersectionStore
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SDIFFSTORE';
27
+ }
28
+ }
includes/predis/src/Command/SetIntersection.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sinter
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetIntersection extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SINTER';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeArguments($arguments);
35
+ }
36
+ }
includes/predis/src/Command/SetIntersectionStore.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sinterstore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetIntersectionStore extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SINTERSTORE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 2 && is_array($arguments[1])) {
35
+ return array_merge(array($arguments[0]), $arguments[1]);
36
+ }
37
+
38
+ return $arguments;
39
+ }
40
+ }
includes/predis/src/Command/SetIsMember.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sismember
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetIsMember extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SISMEMBER';
27
+ }
28
+ }
includes/predis/src/Command/SetMembers.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/smembers
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetMembers extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SMEMBERS';
27
+ }
28
+ }
includes/predis/src/Command/SetMove.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/smove
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetMove extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SMOVE';
27
+ }
28
+ }
includes/predis/src/Command/SetPop.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/spop
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetPop extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SPOP';
27
+ }
28
+ }
includes/predis/src/Command/SetRandomMember.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/srandmember
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetRandomMember extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SRANDMEMBER';
27
+ }
28
+ }
includes/predis/src/Command/SetRemove.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/srem
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetRemove extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SREM';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeVariadic($arguments);
35
+ }
36
+ }
includes/predis/src/Command/SetScan.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sscan
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetScan extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SSCAN';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 3 && is_array($arguments[2])) {
35
+ $options = $this->prepareOptions(array_pop($arguments));
36
+ $arguments = array_merge($arguments, $options);
37
+ }
38
+
39
+ return $arguments;
40
+ }
41
+
42
+ /**
43
+ * Returns a list of options and modifiers compatible with Redis.
44
+ *
45
+ * @param array $options List of options.
46
+ *
47
+ * @return array
48
+ */
49
+ protected function prepareOptions($options)
50
+ {
51
+ $options = array_change_key_case($options, CASE_UPPER);
52
+ $normalized = array();
53
+
54
+ if (!empty($options['MATCH'])) {
55
+ $normalized[] = 'MATCH';
56
+ $normalized[] = $options['MATCH'];
57
+ }
58
+
59
+ if (!empty($options['COUNT'])) {
60
+ $normalized[] = 'COUNT';
61
+ $normalized[] = $options['COUNT'];
62
+ }
63
+
64
+ return $normalized;
65
+ }
66
+ }
includes/predis/src/Command/SetUnion.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sunion
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetUnion extends SetIntersection
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SUNION';
27
+ }
28
+ }
includes/predis/src/Command/SetUnionStore.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/sunionstore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class SetUnionStore extends SetIntersectionStore
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SUNIONSTORE';
27
+ }
28
+ }
includes/predis/src/Command/StringAppend.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/append
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringAppend extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'APPEND';
27
+ }
28
+ }
includes/predis/src/Command/StringBitCount.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/bitcount
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringBitCount extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BITCOUNT';
27
+ }
28
+ }
includes/predis/src/Command/StringBitField.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/bitfield
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringBitField extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BITFIELD';
27
+ }
28
+ }
includes/predis/src/Command/StringBitOp.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/bitop
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringBitOp extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BITOP';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 3 && is_array($arguments[2])) {
35
+ list($operation, $destination) = $arguments;
36
+ $arguments = $arguments[2];
37
+ array_unshift($arguments, $operation, $destination);
38
+ }
39
+
40
+ return $arguments;
41
+ }
42
+ }
includes/predis/src/Command/StringBitPos.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/bitpos
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringBitPos extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'BITPOS';
27
+ }
28
+ }
includes/predis/src/Command/StringDecrement.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/decr
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringDecrement extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'DECR';
27
+ }
28
+ }
includes/predis/src/Command/StringDecrementBy.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/decrby
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringDecrementBy extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'DECRBY';
27
+ }
28
+ }
includes/predis/src/Command/StringGet.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/get
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringGet extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GET';
27
+ }
28
+ }
includes/predis/src/Command/StringGetBit.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/getbit
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringGetBit extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GETBIT';
27
+ }
28
+ }
includes/predis/src/Command/StringGetMultiple.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/mget
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringGetMultiple extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'MGET';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeArguments($arguments);
35
+ }
36
+ }
includes/predis/src/Command/StringGetRange.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/getrange
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringGetRange extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GETRANGE';
27
+ }
28
+ }
includes/predis/src/Command/StringGetSet.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/getset
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringGetSet extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'GETSET';
27
+ }
28
+ }
includes/predis/src/Command/StringIncrement.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/incr
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringIncrement extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'INCR';
27
+ }
28
+ }
includes/predis/src/Command/StringIncrementBy.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/incrby
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringIncrementBy extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'INCRBY';
27
+ }
28
+ }
includes/predis/src/Command/StringIncrementByFloat.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/incrbyfloat
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringIncrementByFloat extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'INCRBYFLOAT';
27
+ }
28
+ }
includes/predis/src/Command/StringPreciseSetExpire.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/psetex
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringPreciseSetExpire extends StringSetExpire
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'PSETEX';
27
+ }
28
+ }
includes/predis/src/Command/StringSet.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/set
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSet extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SET';
27
+ }
28
+ }
includes/predis/src/Command/StringSetBit.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/setbit
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSetBit extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SETBIT';
27
+ }
28
+ }
includes/predis/src/Command/StringSetExpire.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/setex
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSetExpire extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SETEX';
27
+ }
28
+ }
includes/predis/src/Command/StringSetMultiple.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/mset
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSetMultiple extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'MSET';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 1 && is_array($arguments[0])) {
35
+ $flattenedKVs = array();
36
+ $args = $arguments[0];
37
+
38
+ foreach ($args as $k => $v) {
39
+ $flattenedKVs[] = $k;
40
+ $flattenedKVs[] = $v;
41
+ }
42
+
43
+ return $flattenedKVs;
44
+ }
45
+
46
+ return $arguments;
47
+ }
48
+ }
includes/predis/src/Command/StringSetMultiplePreserve.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/msetnx
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSetMultiplePreserve extends StringSetMultiple
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'MSETNX';
27
+ }
28
+ }
includes/predis/src/Command/StringSetPreserve.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/setnx
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSetPreserve extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SETNX';
27
+ }
28
+ }
includes/predis/src/Command/StringSetRange.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/setrange
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSetRange extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SETRANGE';
27
+ }
28
+ }
includes/predis/src/Command/StringStrlen.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/strlen
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringStrlen extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'STRLEN';
27
+ }
28
+ }
includes/predis/src/Command/StringSubstr.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/substr
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class StringSubstr extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'SUBSTR';
27
+ }
28
+ }
includes/predis/src/Command/TransactionDiscard.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/discard
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class TransactionDiscard extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'DISCARD';
27
+ }
28
+ }
includes/predis/src/Command/TransactionExec.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/exec
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class TransactionExec extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'EXEC';
27
+ }
28
+ }
includes/predis/src/Command/TransactionMulti.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/multi
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class TransactionMulti extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'MULTI';
27
+ }
28
+ }
includes/predis/src/Command/TransactionUnwatch.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/unwatch
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class TransactionUnwatch extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'UNWATCH';
27
+ }
28
+ }
includes/predis/src/Command/TransactionWatch.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/watch
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class TransactionWatch extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'WATCH';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (isset($arguments[0]) && is_array($arguments[0])) {
35
+ return $arguments[0];
36
+ }
37
+
38
+ return $arguments;
39
+ }
40
+ }
includes/predis/src/Command/ZSetAdd.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zadd
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetAdd extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZADD';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (is_array(end($arguments))) {
35
+ foreach (array_pop($arguments) as $member => $score) {
36
+ $arguments[] = $score;
37
+ $arguments[] = $member;
38
+ }
39
+ }
40
+
41
+ return $arguments;
42
+ }
43
+ }
includes/predis/src/Command/ZSetCardinality.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zcard
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetCardinality extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZCARD';
27
+ }
28
+ }
includes/predis/src/Command/ZSetCount.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zcount
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetCount extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZCOUNT';
27
+ }
28
+ }
includes/predis/src/Command/ZSetIncrementBy.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zincrby
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetIncrementBy extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZINCRBY';
27
+ }
28
+ }
includes/predis/src/Command/ZSetIntersectionStore.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zinterstore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetIntersectionStore extends ZSetUnionStore
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZINTERSTORE';
27
+ }
28
+ }
includes/predis/src/Command/ZSetLexCount.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zlexcount
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetLexCount extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZLEXCOUNT';
27
+ }
28
+ }
includes/predis/src/Command/ZSetRange.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrange
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRange extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZRANGE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 4) {
35
+ $lastType = gettype($arguments[3]);
36
+
37
+ if ($lastType === 'string' && strtoupper($arguments[3]) === 'WITHSCORES') {
38
+ // Used for compatibility with older versions
39
+ $arguments[3] = array('WITHSCORES' => true);
40
+ $lastType = 'array';
41
+ }
42
+
43
+ if ($lastType === 'array') {
44
+ $options = $this->prepareOptions(array_pop($arguments));
45
+
46
+ return array_merge($arguments, $options);
47
+ }
48
+ }
49
+
50
+ return $arguments;
51
+ }
52
+
53
+ /**
54
+ * Returns a list of options and modifiers compatible with Redis.
55
+ *
56
+ * @param array $options List of options.
57
+ *
58
+ * @return array
59
+ */
60
+ protected function prepareOptions($options)
61
+ {
62
+ $opts = array_change_key_case($options, CASE_UPPER);
63
+ $finalizedOpts = array();
64
+
65
+ if (!empty($opts['WITHSCORES'])) {
66
+ $finalizedOpts[] = 'WITHSCORES';
67
+ }
68
+
69
+ return $finalizedOpts;
70
+ }
71
+
72
+ /**
73
+ * Checks for the presence of the WITHSCORES modifier.
74
+ *
75
+ * @return bool
76
+ */
77
+ protected function withScores()
78
+ {
79
+ $arguments = $this->getArguments();
80
+
81
+ if (count($arguments) < 4) {
82
+ return false;
83
+ }
84
+
85
+ return strtoupper($arguments[3]) === 'WITHSCORES';
86
+ }
87
+
88
+ /**
89
+ * {@inheritdoc}
90
+ */
91
+ public function parseResponse($data)
92
+ {
93
+ if ($this->withScores()) {
94
+ $result = array();
95
+
96
+ for ($i = 0; $i < count($data); ++$i) {
97
+ $result[$data[$i]] = $data[++$i];
98
+ }
99
+
100
+ return $result;
101
+ }
102
+
103
+ return $data;
104
+ }
105
+ }
includes/predis/src/Command/ZSetRangeByLex.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrangebylex
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRangeByLex extends ZSetRange
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZRANGEBYLEX';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function prepareOptions($options)
33
+ {
34
+ $opts = array_change_key_case($options, CASE_UPPER);
35
+ $finalizedOpts = array();
36
+
37
+ if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
38
+ $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);
39
+
40
+ $finalizedOpts[] = 'LIMIT';
41
+ $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0];
42
+ $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1];
43
+ }
44
+
45
+ return $finalizedOpts;
46
+ }
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ protected function withScores()
52
+ {
53
+ return false;
54
+ }
55
+ }
includes/predis/src/Command/ZSetRangeByScore.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrangebyscore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRangeByScore extends ZSetRange
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZRANGEBYSCORE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function prepareOptions($options)
33
+ {
34
+ $opts = array_change_key_case($options, CASE_UPPER);
35
+ $finalizedOpts = array();
36
+
37
+ if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
38
+ $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);
39
+
40
+ $finalizedOpts[] = 'LIMIT';
41
+ $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0];
42
+ $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1];
43
+ }
44
+
45
+ return array_merge($finalizedOpts, parent::prepareOptions($options));
46
+ }
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ protected function withScores()
52
+ {
53
+ $arguments = $this->getArguments();
54
+
55
+ for ($i = 3; $i < count($arguments); ++$i) {
56
+ switch (strtoupper($arguments[$i])) {
57
+ case 'WITHSCORES':
58
+ return true;
59
+
60
+ case 'LIMIT':
61
+ $i += 2;
62
+ break;
63
+ }
64
+ }
65
+
66
+ return false;
67
+ }
68
+ }
includes/predis/src/Command/ZSetRank.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrank
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRank extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZRANK';
27
+ }
28
+ }
includes/predis/src/Command/ZSetRemove.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrem
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRemove extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREM';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ return self::normalizeVariadic($arguments);
35
+ }
36
+ }
includes/predis/src/Command/ZSetRemoveRangeByLex.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zremrangebylex
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRemoveRangeByLex extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREMRANGEBYLEX';
27
+ }
28
+ }
includes/predis/src/Command/ZSetRemoveRangeByRank.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zremrangebyrank
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRemoveRangeByRank extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREMRANGEBYRANK';
27
+ }
28
+ }
includes/predis/src/Command/ZSetRemoveRangeByScore.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zremrangebyscore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetRemoveRangeByScore extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREMRANGEBYSCORE';
27
+ }
28
+ }
includes/predis/src/Command/ZSetReverseRange.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrevrange
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetReverseRange extends ZSetRange
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREVRANGE';
27
+ }
28
+ }
includes/predis/src/Command/ZSetReverseRangeByLex.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrevrangebylex
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetReverseRangeByLex extends ZSetRangeByLex
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREVRANGEBYLEX';
27
+ }
28
+ }
includes/predis/src/Command/ZSetReverseRangeByScore.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrevrangebyscore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetReverseRangeByScore extends ZSetRangeByScore
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREVRANGEBYSCORE';
27
+ }
28
+ }
includes/predis/src/Command/ZSetReverseRank.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zrevrank
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetReverseRank extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZREVRANK';
27
+ }
28
+ }
includes/predis/src/Command/ZSetScan.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zscan
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetScan extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZSCAN';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ if (count($arguments) === 3 && is_array($arguments[2])) {
35
+ $options = $this->prepareOptions(array_pop($arguments));
36
+ $arguments = array_merge($arguments, $options);
37
+ }
38
+
39
+ return $arguments;
40
+ }
41
+
42
+ /**
43
+ * Returns a list of options and modifiers compatible with Redis.
44
+ *
45
+ * @param array $options List of options.
46
+ *
47
+ * @return array
48
+ */
49
+ protected function prepareOptions($options)
50
+ {
51
+ $options = array_change_key_case($options, CASE_UPPER);
52
+ $normalized = array();
53
+
54
+ if (!empty($options['MATCH'])) {
55
+ $normalized[] = 'MATCH';
56
+ $normalized[] = $options['MATCH'];
57
+ }
58
+
59
+ if (!empty($options['COUNT'])) {
60
+ $normalized[] = 'COUNT';
61
+ $normalized[] = $options['COUNT'];
62
+ }
63
+
64
+ return $normalized;
65
+ }
66
+
67
+ /**
68
+ * {@inheritdoc}
69
+ */
70
+ public function parseResponse($data)
71
+ {
72
+ if (is_array($data)) {
73
+ $members = $data[1];
74
+ $result = array();
75
+
76
+ for ($i = 0; $i < count($members); ++$i) {
77
+ $result[$members[$i]] = (float) $members[++$i];
78
+ }
79
+
80
+ $data[1] = $result;
81
+ }
82
+
83
+ return $data;
84
+ }
85
+ }
includes/predis/src/Command/ZSetScore.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zscore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetScore extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZSCORE';
27
+ }
28
+ }
includes/predis/src/Command/ZSetUnionStore.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Command;
13
+
14
+ /**
15
+ * @link http://redis.io/commands/zunionstore
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class ZSetUnionStore extends Command
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getId()
25
+ {
26
+ return 'ZUNIONSTORE';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ protected function filterArguments(array $arguments)
33
+ {
34
+ $options = array();
35
+ $argc = count($arguments);
36
+
37
+ if ($argc > 2 && is_array($arguments[$argc - 1])) {
38
+ $options = $this->prepareOptions(array_pop($arguments));
39
+ }
40
+
41
+ if (is_array($arguments[1])) {
42
+ $arguments = array_merge(
43
+ array($arguments[0], count($arguments[1])),
44
+ $arguments[1]
45
+ );
46
+ }
47
+
48
+ return array_merge($arguments, $options);
49
+ }
50
+
51
+ /**
52
+ * Returns a list of options and modifiers compatible with Redis.
53
+ *
54
+ * @param array $options List of options.
55
+ *
56
+ * @return array
57
+ */
58
+ private function prepareOptions($options)
59
+ {
60
+ $opts = array_change_key_case($options, CASE_UPPER);
61
+ $finalizedOpts = array();
62
+
63
+ if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) {
64
+ $finalizedOpts[] = 'WEIGHTS';
65
+
66
+ foreach ($opts['WEIGHTS'] as $weight) {
67
+ $finalizedOpts[] = $weight;
68
+ }
69
+ }
70
+
71
+ if (isset($opts['AGGREGATE'])) {
72
+ $finalizedOpts[] = 'AGGREGATE';
73
+ $finalizedOpts[] = $opts['AGGREGATE'];
74
+ }
75
+
76
+ return $finalizedOpts;
77
+ }
78
+ }
includes/predis/src/CommunicationException.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ use Predis\Connection\NodeConnectionInterface;
15
+
16
+ /**
17
+ * Base exception class for network-related errors.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ abstract class CommunicationException extends PredisException
22
+ {
23
+ private $connection;
24
+
25
+ /**
26
+ * @param NodeConnectionInterface $connection Connection that generated the exception.
27
+ * @param string $message Error message.
28
+ * @param int $code Error code.
29
+ * @param \Exception $innerException Inner exception for wrapping the original error.
30
+ */
31
+ public function __construct(
32
+ NodeConnectionInterface $connection,
33
+ $message = null,
34
+ $code = null,
35
+ \Exception $innerException = null
36
+ ) {
37
+ parent::__construct($message, $code, $innerException);
38
+ $this->connection = $connection;
39
+ }
40
+
41
+ /**
42
+ * Gets the connection that generated the exception.
43
+ *
44
+ * @return NodeConnectionInterface
45
+ */
46
+ public function getConnection()
47
+ {
48
+ return $this->connection;
49
+ }
50
+
51
+ /**
52
+ * Indicates if the receiver should reset the underlying connection.
53
+ *
54
+ * @return bool
55
+ */
56
+ public function shouldResetConnection()
57
+ {
58
+ return true;
59
+ }
60
+
61
+ /**
62
+ * Helper method to handle exceptions generated by a connection object.
63
+ *
64
+ * @param CommunicationException $exception Exception.
65
+ *
66
+ * @throws CommunicationException
67
+ */
68
+ public static function handle(CommunicationException $exception)
69
+ {
70
+ if ($exception->shouldResetConnection()) {
71
+ $connection = $exception->getConnection();
72
+
73
+ if ($connection->isConnected()) {
74
+ $connection->disconnect();
75
+ }
76
+ }
77
+
78
+ throw $exception;
79
+ }
80
+ }
includes/predis/src/Configuration/ClusterOption.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ use Predis\Connection\Aggregate\ClusterInterface;
15
+ use Predis\Connection\Aggregate\PredisCluster;
16
+ use Predis\Connection\Aggregate\RedisCluster;
17
+
18
+ /**
19
+ * Configures an aggregate connection used for clustering
20
+ * multiple Redis nodes using various implementations with
21
+ * different algorithms or strategies.
22
+ *
23
+ * @author Daniele Alessandri <suppakilla@gmail.com>
24
+ */
25
+ class ClusterOption implements OptionInterface
26
+ {
27
+ /**
28
+ * Creates a new cluster connection from on a known descriptive name.
29
+ *
30
+ * @param OptionsInterface $options Instance of the client options.
31
+ * @param string $id Descriptive identifier of the cluster type (`predis`, `redis-cluster`)
32
+ *
33
+ * @return ClusterInterface|null
34
+ */
35
+ protected function createByDescription(OptionsInterface $options, $id)
36
+ {
37
+ switch ($id) {
38
+ case 'predis':
39
+ case 'predis-cluster':
40
+ return new PredisCluster();
41
+
42
+ case 'redis':
43
+ case 'redis-cluster':
44
+ return new RedisCluster($options->connections);
45
+
46
+ default:
47
+ return;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * {@inheritdoc}
53
+ */
54
+ public function filter(OptionsInterface $options, $value)
55
+ {
56
+ if (is_string($value)) {
57
+ $value = $this->createByDescription($options, $value);
58
+ }
59
+
60
+ if (!$value instanceof ClusterInterface) {
61
+ throw new \InvalidArgumentException(
62
+ "An instance of type 'Predis\Connection\Aggregate\ClusterInterface' was expected."
63
+ );
64
+ }
65
+
66
+ return $value;
67
+ }
68
+
69
+ /**
70
+ * {@inheritdoc}
71
+ */
72
+ public function getDefault(OptionsInterface $options)
73
+ {
74
+ return new PredisCluster();
75
+ }
76
+ }
includes/predis/src/Configuration/ConnectionFactoryOption.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ use Predis\Connection\Factory;
15
+ use Predis\Connection\FactoryInterface;
16
+
17
+ /**
18
+ * Configures a connection factory used by the client to create new connection
19
+ * instances for single Redis nodes.
20
+ *
21
+ * @author Daniele Alessandri <suppakilla@gmail.com>
22
+ */
23
+ class ConnectionFactoryOption implements OptionInterface
24
+ {
25
+ /**
26
+ * {@inheritdoc}
27
+ */
28
+ public function filter(OptionsInterface $options, $value)
29
+ {
30
+ if ($value instanceof FactoryInterface) {
31
+ return $value;
32
+ } elseif (is_array($value)) {
33
+ $factory = $this->getDefault($options);
34
+
35
+ foreach ($value as $scheme => $initializer) {
36
+ $factory->define($scheme, $initializer);
37
+ }
38
+
39
+ return $factory;
40
+ } else {
41
+ throw new \InvalidArgumentException(
42
+ 'Invalid value provided for the connections option.'
43
+ );
44
+ }
45
+ }
46
+
47
+ /**
48
+ * {@inheritdoc}
49
+ */
50
+ public function getDefault(OptionsInterface $options)
51
+ {
52
+ $factory = new Factory();
53
+
54
+ if ($options->defined('parameters')) {
55
+ $factory->setDefaultParameters($options->parameters);
56
+ }
57
+
58
+ return $factory;
59
+ }
60
+ }
includes/predis/src/Configuration/ExceptionsOption.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ /**
15
+ * Configures whether consumers (such as the client) should throw exceptions on
16
+ * Redis errors (-ERR responses) or just return instances of error responses.
17
+ *
18
+ * @author Daniele Alessandri <suppakilla@gmail.com>
19
+ */
20
+ class ExceptionsOption implements OptionInterface
21
+ {
22
+ /**
23
+ * {@inheritdoc}
24
+ */
25
+ public function filter(OptionsInterface $options, $value)
26
+ {
27
+ return filter_var($value, FILTER_VALIDATE_BOOLEAN);
28
+ }
29
+
30
+ /**
31
+ * {@inheritdoc}
32
+ */
33
+ public function getDefault(OptionsInterface $options)
34
+ {
35
+ return true;
36
+ }
37
+ }
includes/predis/src/Configuration/OptionInterface.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ /**
15
+ * Defines an handler used by Predis\Configuration\Options to filter, validate
16
+ * or return default values for a given option.
17
+ *
18
+ * @author Daniele Alessandri <suppakilla@gmail.com>
19
+ */
20
+ interface OptionInterface
21
+ {
22
+ /**
23
+ * Filters and validates the passed value.
24
+ *
25
+ * @param OptionsInterface $options Options container.
26
+ * @param mixed $value Input value.
27
+ *
28
+ * @return mixed
29
+ */
30
+ public function filter(OptionsInterface $options, $value);
31
+
32
+ /**
33
+ * Returns the default value for the option.
34
+ *
35
+ * @param OptionsInterface $options Options container.
36
+ *
37
+ * @return mixed
38
+ */
39
+ public function getDefault(OptionsInterface $options);
40
+ }
includes/predis/src/Configuration/Options.php ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ /**
15
+ * Manages Predis options with filtering, conversion and lazy initialization of
16
+ * values using a mini-DI container approach.
17
+ *
18
+ * {@inheritdoc}
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ class Options implements OptionsInterface
23
+ {
24
+ protected $input;
25
+ protected $options;
26
+ protected $handlers;
27
+
28
+ /**
29
+ * @param array $options Array of options with their values
30
+ */
31
+ public function __construct(array $options = array())
32
+ {
33
+ $this->input = $options;
34
+ $this->options = array();
35
+ $this->handlers = $this->getHandlers();
36
+ }
37
+
38
+ /**
39
+ * Ensures that the default options are initialized.
40
+ *
41
+ * @return array
42
+ */
43
+ protected function getHandlers()
44
+ {
45
+ return array(
46
+ 'cluster' => 'Predis\Configuration\ClusterOption',
47
+ 'connections' => 'Predis\Configuration\ConnectionFactoryOption',
48
+ 'exceptions' => 'Predis\Configuration\ExceptionsOption',
49
+ 'prefix' => 'Predis\Configuration\PrefixOption',
50
+ 'profile' => 'Predis\Configuration\ProfileOption',
51
+ 'replication' => 'Predis\Configuration\ReplicationOption',
52
+ );
53
+ }
54
+
55
+ /**
56
+ * {@inheritdoc}
57
+ */
58
+ public function getDefault($option)
59
+ {
60
+ if (isset($this->handlers[$option])) {
61
+ $handler = $this->handlers[$option];
62
+ $handler = new $handler();
63
+
64
+ return $handler->getDefault($this);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * {@inheritdoc}
70
+ */
71
+ public function defined($option)
72
+ {
73
+ return
74
+ array_key_exists($option, $this->options) ||
75
+ array_key_exists($option, $this->input)
76
+ ;
77
+ }
78
+
79
+ /**
80
+ * {@inheritdoc}
81
+ */
82
+ public function __isset($option)
83
+ {
84
+ return (
85
+ array_key_exists($option, $this->options) ||
86
+ array_key_exists($option, $this->input)
87
+ ) && $this->__get($option) !== null;
88
+ }
89
+
90
+ /**
91
+ * {@inheritdoc}
92
+ */
93
+ public function __get($option)
94
+ {
95
+ if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
96
+ return $this->options[$option];
97
+ }
98
+
99
+ if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
100
+ $value = $this->input[$option];
101
+ unset($this->input[$option]);
102
+
103
+ if (is_object($value) && method_exists($value, '__invoke')) {
104
+ $value = $value($this, $option);
105
+ }
106
+
107
+ if (isset($this->handlers[$option])) {
108
+ $handler = $this->handlers[$option];
109
+ $handler = new $handler();
110
+ $value = $handler->filter($this, $value);
111
+ }
112
+
113
+ return $this->options[$option] = $value;
114
+ }
115
+
116
+ if (isset($this->handlers[$option])) {
117
+ return $this->options[$option] = $this->getDefault($option);
118
+ }
119
+
120
+ return;
121
+ }
122
+ }
includes/predis/src/Configuration/OptionsInterface.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ /**
15
+ * Interface defining a container for client options.
16
+ *
17
+ * @property-read mixed aggregate Custom connection aggregator.
18
+ * @property-read mixed cluster Aggregate connection for clustering.
19
+ * @property-read mixed connections Connection factory.
20
+ * @property-read mixed exceptions Toggles exceptions in client for -ERR responses.
21
+ * @property-read mixed prefix Key prefixing strategy using the given prefix.
22
+ * @property-read mixed profile Server profile.
23
+ * @property-read mixed replication Aggregate connection for replication.
24
+ *
25
+ * @author Daniele Alessandri <suppakilla@gmail.com>
26
+ */
27
+ interface OptionsInterface
28
+ {
29
+ /**
30
+ * Returns the default value for the given option.
31
+ *
32
+ * @param string $option Name of the option.
33
+ *
34
+ * @return mixed|null
35
+ */
36
+ public function getDefault($option);
37
+
38
+ /**
39
+ * Checks if the given option has been set by the user upon initialization.
40
+ *
41
+ * @param string $option Name of the option.
42
+ *
43
+ * @return bool
44
+ */
45
+ public function defined($option);
46
+
47
+ /**
48
+ * Checks if the given option has been set and does not evaluate to NULL.
49
+ *
50
+ * @param string $option Name of the option.
51
+ *
52
+ * @return bool
53
+ */
54
+ public function __isset($option);
55
+
56
+ /**
57
+ * Returns the value of the given option.
58
+ *
59
+ * @param string $option Name of the option.
60
+ *
61
+ * @return mixed|null
62
+ */
63
+ public function __get($option);
64
+ }
includes/predis/src/Configuration/PrefixOption.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ use Predis\Command\Processor\KeyPrefixProcessor;
15
+ use Predis\Command\Processor\ProcessorInterface;
16
+
17
+ /**
18
+ * Configures a command processor that apply the specified prefix string to a
19
+ * series of Redis commands considered prefixable.
20
+ *
21
+ * @author Daniele Alessandri <suppakilla@gmail.com>
22
+ */
23
+ class PrefixOption implements OptionInterface
24
+ {
25
+ /**
26
+ * {@inheritdoc}
27
+ */
28
+ public function filter(OptionsInterface $options, $value)
29
+ {
30
+ if ($value instanceof ProcessorInterface) {
31
+ return $value;
32
+ }
33
+
34
+ return new KeyPrefixProcessor($value);
35
+ }
36
+
37
+ /**
38
+ * {@inheritdoc}
39
+ */
40
+ public function getDefault(OptionsInterface $options)
41
+ {
42
+ // NOOP
43
+ }
44
+ }
includes/predis/src/Configuration/ProfileOption.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ use Predis\Profile\Factory;
15
+ use Predis\Profile\ProfileInterface;
16
+ use Predis\Profile\RedisProfile;
17
+
18
+ /**
19
+ * Configures the server profile to be used by the client to create command
20
+ * instances depending on the specified version of the Redis server.
21
+ *
22
+ * @author Daniele Alessandri <suppakilla@gmail.com>
23
+ */
24
+ class ProfileOption implements OptionInterface
25
+ {
26
+ /**
27
+ * Sets the commands processors that need to be applied to the profile.
28
+ *
29
+ * @param OptionsInterface $options Client options.
30
+ * @param ProfileInterface $profile Server profile.
31
+ */
32
+ protected function setProcessors(OptionsInterface $options, ProfileInterface $profile)
33
+ {
34
+ if (isset($options->prefix) && $profile instanceof RedisProfile) {
35
+ // NOTE: directly using __get('prefix') is actually a workaround for
36
+ // HHVM 2.3.0. It's correct and respects the options interface, it's
37
+ // just ugly. We will remove this hack when HHVM will fix re-entrant
38
+ // calls to __get() once and for all.
39
+
40
+ $profile->setProcessor($options->__get('prefix'));
41
+ }
42
+ }
43
+
44
+ /**
45
+ * {@inheritdoc}
46
+ */
47
+ public function filter(OptionsInterface $options, $value)
48
+ {
49
+ if (is_string($value)) {
50
+ $value = Factory::get($value);
51
+ $this->setProcessors($options, $value);
52
+ } elseif (!$value instanceof ProfileInterface) {
53
+ throw new \InvalidArgumentException('Invalid value for the profile option.');
54
+ }
55
+
56
+ return $value;
57
+ }
58
+
59
+ /**
60
+ * {@inheritdoc}
61
+ */
62
+ public function getDefault(OptionsInterface $options)
63
+ {
64
+ $profile = Factory::getDefault();
65
+ $this->setProcessors($options, $profile);
66
+
67
+ return $profile;
68
+ }
69
+ }
includes/predis/src/Configuration/ReplicationOption.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Configuration;
13
+
14
+ use Predis\Connection\Aggregate\MasterSlaveReplication;
15
+ use Predis\Connection\Aggregate\ReplicationInterface;
16
+ use Predis\Connection\Aggregate\SentinelReplication;
17
+
18
+ /**
19
+ * Configures an aggregate connection used for master/slave replication among
20
+ * multiple Redis nodes.
21
+ *
22
+ * @author Daniele Alessandri <suppakilla@gmail.com>
23
+ */
24
+ class ReplicationOption implements OptionInterface
25
+ {
26
+ /**
27
+ * {@inheritdoc}
28
+ *
29
+ * @todo There's more code than needed due to a bug in filter_var() as
30
+ * discussed here https://bugs.php.net/bug.php?id=49510 and different
31
+ * behaviours when encountering NULL values on PHP 5.3.
32
+ */
33
+ public function filter(OptionsInterface $options, $value)
34
+ {
35
+ if ($value instanceof ReplicationInterface) {
36
+ return $value;
37
+ }
38
+
39
+ if (is_bool($value) || $value === null) {
40
+ return $value ? $this->getDefault($options) : null;
41
+ }
42
+
43
+ if ($value === 'sentinel') {
44
+ return function ($sentinels, $options) {
45
+ return new SentinelReplication($options->service, $sentinels, $options->connections);
46
+ };
47
+ }
48
+
49
+ if (
50
+ !is_object($value) &&
51
+ null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
52
+ ) {
53
+ return $asbool ? $this->getDefault($options) : null;
54
+ }
55
+
56
+ throw new \InvalidArgumentException(
57
+ "An instance of type 'Predis\Connection\Aggregate\ReplicationInterface' was expected."
58
+ );
59
+ }
60
+
61
+ /**
62
+ * {@inheritdoc}
63
+ */
64
+ public function getDefault(OptionsInterface $options)
65
+ {
66
+ $replication = new MasterSlaveReplication();
67
+
68
+ if ($options->autodiscovery) {
69
+ $replication->setConnectionFactory($options->connections);
70
+ $replication->setAutoDiscovery(true);
71
+ }
72
+
73
+ return $replication;
74
+ }
75
+ }
includes/predis/src/Connection/AbstractConnection.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\CommunicationException;
16
+ use Predis\Protocol\ProtocolException;
17
+
18
+ /**
19
+ * Base class with the common logic used by connection classes to communicate
20
+ * with Redis.
21
+ *
22
+ * @author Daniele Alessandri <suppakilla@gmail.com>
23
+ */
24
+ abstract class AbstractConnection implements NodeConnectionInterface
25
+ {
26
+ private $resource;
27
+ private $cachedId;
28
+
29
+ protected $parameters;
30
+ protected $initCommands = array();
31
+
32
+ /**
33
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
34
+ */
35
+ public function __construct(ParametersInterface $parameters)
36
+ {
37
+ $this->parameters = $this->assertParameters($parameters);
38
+ }
39
+
40
+ /**
41
+ * Disconnects from the server and destroys the underlying resource when
42
+ * PHP's garbage collector kicks in.
43
+ */
44
+ public function __destruct()
45
+ {
46
+ $this->disconnect();
47
+ }
48
+
49
+ /**
50
+ * Checks some of the parameters used to initialize the connection.
51
+ *
52
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
53
+ *
54
+ * @throws \InvalidArgumentException
55
+ *
56
+ * @return ParametersInterface
57
+ */
58
+ abstract protected function assertParameters(ParametersInterface $parameters);
59
+
60
+ /**
61
+ * Creates the underlying resource used to communicate with Redis.
62
+ *
63
+ * @return mixed
64
+ */
65
+ abstract protected function createResource();
66
+
67
+ /**
68
+ * {@inheritdoc}
69
+ */
70
+ public function isConnected()
71
+ {
72
+ return isset($this->resource);
73
+ }
74
+
75
+ /**
76
+ * {@inheritdoc}
77
+ */
78
+ public function connect()
79
+ {
80
+ if (!$this->isConnected()) {
81
+ $this->resource = $this->createResource();
82
+
83
+ return true;
84
+ }
85
+
86
+ return false;
87
+ }
88
+
89
+ /**
90
+ * {@inheritdoc}
91
+ */
92
+ public function disconnect()
93
+ {
94
+ unset($this->resource);
95
+ }
96
+
97
+ /**
98
+ * {@inheritdoc}
99
+ */
100
+ public function addConnectCommand(CommandInterface $command)
101
+ {
102
+ $this->initCommands[] = $command;
103
+ }
104
+
105
+ /**
106
+ * {@inheritdoc}
107
+ */
108
+ public function executeCommand(CommandInterface $command)
109
+ {
110
+ $this->writeRequest($command);
111
+
112
+ return $this->readResponse($command);
113
+ }
114
+
115
+ /**
116
+ * {@inheritdoc}
117
+ */
118
+ public function readResponse(CommandInterface $command)
119
+ {
120
+ return $this->read();
121
+ }
122
+
123
+ /**
124
+ * Helper method that returns an exception message augmented with useful
125
+ * details from the connection parameters.
126
+ *
127
+ * @param string $message Error message.
128
+ *
129
+ * @return string
130
+ */
131
+ private function createExceptionMessage($message)
132
+ {
133
+ $parameters = $this->parameters;
134
+
135
+ if ($parameters->scheme === 'unix') {
136
+ return "$message [$parameters->scheme:$parameters->path]";
137
+ }
138
+
139
+ if (filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
140
+ return "$message [$parameters->scheme://[$parameters->host]:$parameters->port]";
141
+ }
142
+
143
+ return "$message [$parameters->scheme://$parameters->host:$parameters->port]";
144
+ }
145
+
146
+ /**
147
+ * Helper method to handle connection errors.
148
+ *
149
+ * @param string $message Error message.
150
+ * @param int $code Error code.
151
+ */
152
+ protected function onConnectionError($message, $code = null)
153
+ {
154
+ CommunicationException::handle(
155
+ new ConnectionException($this, static::createExceptionMessage($message), $code)
156
+ );
157
+ }
158
+
159
+ /**
160
+ * Helper method to handle protocol errors.
161
+ *
162
+ * @param string $message Error message.
163
+ */
164
+ protected function onProtocolError($message)
165
+ {
166
+ CommunicationException::handle(
167
+ new ProtocolException($this, static::createExceptionMessage($message))
168
+ );
169
+ }
170
+
171
+ /**
172
+ * {@inheritdoc}
173
+ */
174
+ public function getResource()
175
+ {
176
+ if (isset($this->resource)) {
177
+ return $this->resource;
178
+ }
179
+
180
+ $this->connect();
181
+
182
+ return $this->resource;
183
+ }
184
+
185
+ /**
186
+ * {@inheritdoc}
187
+ */
188
+ public function getParameters()
189
+ {
190
+ return $this->parameters;
191
+ }
192
+
193
+ /**
194
+ * Gets an identifier for the connection.
195
+ *
196
+ * @return string
197
+ */
198
+ protected function getIdentifier()
199
+ {
200
+ if ($this->parameters->scheme === 'unix') {
201
+ return $this->parameters->path;
202
+ }
203
+
204
+ return "{$this->parameters->host}:{$this->parameters->port}";
205
+ }
206
+
207
+ /**
208
+ * {@inheritdoc}
209
+ */
210
+ public function __toString()
211
+ {
212
+ if (!isset($this->cachedId)) {
213
+ $this->cachedId = $this->getIdentifier();
214
+ }
215
+
216
+ return $this->cachedId;
217
+ }
218
+
219
+ /**
220
+ * {@inheritdoc}
221
+ */
222
+ public function __sleep()
223
+ {
224
+ return array('parameters', 'initCommands');
225
+ }
226
+ }
includes/predis/src/Connection/Aggregate/ClusterInterface.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection\Aggregate;
13
+
14
+ use Predis\Connection\AggregateConnectionInterface;
15
+
16
+ /**
17
+ * Defines a cluster of Redis servers formed by aggregating multiple connection
18
+ * instances to single Redis nodes.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ interface ClusterInterface extends AggregateConnectionInterface
23
+ {
24
+ }
includes/predis/src/Connection/Aggregate/MasterSlaveReplication.php ADDED
@@ -0,0 +1,509 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection\Aggregate;
13
+
14
+ use Predis\ClientException;
15
+ use Predis\Command\CommandInterface;
16
+ use Predis\Command\RawCommand;
17
+ use Predis\Connection\ConnectionException;
18
+ use Predis\Connection\FactoryInterface;
19
+ use Predis\Connection\NodeConnectionInterface;
20
+ use Predis\Replication\MissingMasterException;
21
+ use Predis\Replication\ReplicationStrategy;
22
+ use Predis\Response\ErrorInterface as ResponseErrorInterface;
23
+
24
+ /**
25
+ * Aggregate connection handling replication of Redis nodes configured in a
26
+ * single master / multiple slaves setup.
27
+ *
28
+ * @author Daniele Alessandri <suppakilla@gmail.com>
29
+ */
30
+ class MasterSlaveReplication implements ReplicationInterface
31
+ {
32
+ /**
33
+ * @var ReplicationStrategy
34
+ */
35
+ protected $strategy;
36
+
37
+ /**
38
+ * @var NodeConnectionInterface
39
+ */
40
+ protected $master;
41
+
42
+ /**
43
+ * @var NodeConnectionInterface[]
44
+ */
45
+ protected $slaves = array();
46
+
47
+ /**
48
+ * @var NodeConnectionInterface
49
+ */
50
+ protected $current;
51
+
52
+ /**
53
+ * @var bool
54
+ */
55
+ protected $autoDiscovery = false;
56
+
57
+ /**
58
+ * @var FactoryInterface
59
+ */
60
+ protected $connectionFactory;
61
+
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ public function __construct(ReplicationStrategy $strategy = null)
66
+ {
67
+ $this->strategy = $strategy ?: new ReplicationStrategy();
68
+ }
69
+
70
+ /**
71
+ * Configures the automatic discovery of the replication configuration on failure.
72
+ *
73
+ * @param bool $value Enable or disable auto discovery.
74
+ */
75
+ public function setAutoDiscovery($value)
76
+ {
77
+ if (!$this->connectionFactory) {
78
+ throw new ClientException('Automatic discovery requires a connection factory');
79
+ }
80
+
81
+ $this->autoDiscovery = (bool) $value;
82
+ }
83
+
84
+ /**
85
+ * Sets the connection factory used to create the connections by the auto
86
+ * discovery procedure.
87
+ *
88
+ * @param FactoryInterface $connectionFactory Connection factory instance.
89
+ */
90
+ public function setConnectionFactory(FactoryInterface $connectionFactory)
91
+ {
92
+ $this->connectionFactory = $connectionFactory;
93
+ }
94
+
95
+ /**
96
+ * Resets the connection state.
97
+ */
98
+ protected function reset()
99
+ {
100
+ $this->current = null;
101
+ }
102
+
103
+ /**
104
+ * {@inheritdoc}
105
+ */
106
+ public function add(NodeConnectionInterface $connection)
107
+ {
108
+ $alias = $connection->getParameters()->alias;
109
+
110
+ if ($alias === 'master') {
111
+ $this->master = $connection;
112
+ } else {
113
+ $this->slaves[$alias ?: "slave-$connection"] = $connection;
114
+ }
115
+
116
+ $this->reset();
117
+ }
118
+
119
+ /**
120
+ * {@inheritdoc}
121
+ */
122
+ public function remove(NodeConnectionInterface $connection)
123
+ {
124
+ if ($connection->getParameters()->alias === 'master') {
125
+ $this->master = null;
126
+ $this->reset();
127
+
128
+ return true;
129
+ } else {
130
+ if (($id = array_search($connection, $this->slaves, true)) !== false) {
131
+ unset($this->slaves[$id]);
132
+ $this->reset();
133
+
134
+ return true;
135
+ }
136
+ }
137
+
138
+ return false;
139
+ }
140
+
141
+ /**
142
+ * {@inheritdoc}
143
+ */
144
+ public function getConnection(CommandInterface $command)
145
+ {
146
+ if (!$this->current) {
147
+ if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) {
148
+ $this->current = $slave;
149
+ } else {
150
+ $this->current = $this->getMasterOrDie();
151
+ }
152
+
153
+ return $this->current;
154
+ }
155
+
156
+ if ($this->current === $master = $this->getMasterOrDie()) {
157
+ return $master;
158
+ }
159
+
160
+ if (!$this->strategy->isReadOperation($command) || !$this->slaves) {
161
+ $this->current = $master;
162
+ }
163
+
164
+ return $this->current;
165
+ }
166
+
167
+ /**
168
+ * {@inheritdoc}
169
+ */
170
+ public function getConnectionById($connectionId)
171
+ {
172
+ if ($connectionId === 'master') {
173
+ return $this->master;
174
+ }
175
+
176
+ if (isset($this->slaves[$connectionId])) {
177
+ return $this->slaves[$connectionId];
178
+ }
179
+
180
+ return;
181
+ }
182
+
183
+ /**
184
+ * {@inheritdoc}
185
+ */
186
+ public function switchTo($connection)
187
+ {
188
+ if (!$connection instanceof NodeConnectionInterface) {
189
+ $connection = $this->getConnectionById($connection);
190
+ }
191
+
192
+ if (!$connection) {
193
+ throw new \InvalidArgumentException('Invalid connection or connection not found.');
194
+ }
195
+
196
+ if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) {
197
+ throw new \InvalidArgumentException('Invalid connection or connection not found.');
198
+ }
199
+
200
+ $this->current = $connection;
201
+ }
202
+
203
+ /**
204
+ * Switches to the master server.
205
+ */
206
+ public function switchToMaster()
207
+ {
208
+ $this->switchTo('master');
209
+ }
210
+
211
+ /**
212
+ * Switches to a random slave server.
213
+ */
214
+ public function switchToSlave()
215
+ {
216
+ $connection = $this->pickSlave();
217
+ $this->switchTo($connection);
218
+ }
219
+
220
+ /**
221
+ * {@inheritdoc}
222
+ */
223
+ public function getCurrent()
224
+ {
225
+ return $this->current;
226
+ }
227
+
228
+ /**
229
+ * {@inheritdoc}
230
+ */
231
+ public function getMaster()
232
+ {
233
+ return $this->master;
234
+ }
235
+
236
+ /**
237
+ * Returns the connection associated to the master server.
238
+ *
239
+ * @return NodeConnectionInterface
240
+ */
241
+ private function getMasterOrDie()
242
+ {
243
+ if (!$connection = $this->getMaster()) {
244
+ throw new MissingMasterException('No master server available for replication');
245
+ }
246
+
247
+ return $connection;
248
+ }
249
+
250
+ /**
251
+ * {@inheritdoc}
252
+ */
253
+ public function getSlaves()
254
+ {
255
+ return array_values($this->slaves);
256
+ }
257
+
258
+ /**
259
+ * Returns the underlying replication strategy.
260
+ *
261
+ * @return ReplicationStrategy
262
+ */
263
+ public function getReplicationStrategy()
264
+ {
265
+ return $this->strategy;
266
+ }
267
+
268
+ /**
269
+ * Returns a random slave.
270
+ *
271
+ * @return NodeConnectionInterface
272
+ */
273
+ protected function pickSlave()
274
+ {
275
+ if ($this->slaves) {
276
+ return $this->slaves[array_rand($this->slaves)];
277
+ }
278
+ }
279
+
280
+ /**
281
+ * {@inheritdoc}
282
+ */
283
+ public function isConnected()
284
+ {
285
+ return $this->current ? $this->current->isConnected() : false;
286
+ }
287
+
288
+ /**
289
+ * {@inheritdoc}
290
+ */
291
+ public function connect()
292
+ {
293
+ if (!$this->current) {
294
+ if (!$this->current = $this->pickSlave()) {
295
+ if (!$this->current = $this->getMaster()) {
296
+ throw new ClientException('No available connection for replication');
297
+ }
298
+ }
299
+ }
300
+
301
+ $this->current->connect();
302
+ }
303
+
304
+ /**
305
+ * {@inheritdoc}
306
+ */
307
+ public function disconnect()
308
+ {
309
+ if ($this->master) {
310
+ $this->master->disconnect();
311
+ }
312
+
313
+ foreach ($this->slaves as $connection) {
314
+ $connection->disconnect();
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Handles response from INFO.
320
+ *
321
+ * @param string $response
322
+ *
323
+ * @return array
324
+ */
325
+ private function handleInfoResponse($response)
326
+ {
327
+ $info = array();
328
+
329
+ foreach (preg_split('/\r?\n/', $response) as $row) {
330
+ if (strpos($row, ':') === false) {
331
+ continue;
332
+ }
333
+
334
+ list($k, $v) = explode(':', $row, 2);
335
+ $info[$k] = $v;
336
+ }
337
+
338
+ return $info;
339
+ }
340
+
341
+ /**
342
+ * Fetches the replication configuration from one of the servers.
343
+ */
344
+ public function discover()
345
+ {
346
+ if (!$this->connectionFactory) {
347
+ throw new ClientException('Discovery requires a connection factory');
348
+ }
349
+
350
+ RETRY_FETCH: {
351
+ try {
352
+ if ($connection = $this->getMaster()) {
353
+ $this->discoverFromMaster($connection, $this->connectionFactory);
354
+ } elseif ($connection = $this->pickSlave()) {
355
+ $this->discoverFromSlave($connection, $this->connectionFactory);
356
+ } else {
357
+ throw new ClientException('No connection available for discovery');
358
+ }
359
+ } catch (ConnectionException $exception) {
360
+ $this->remove($connection);
361
+ goto RETRY_FETCH;
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Discovers the replication configuration by contacting the master node.
368
+ *
369
+ * @param NodeConnectionInterface $connection Connection to the master node.
370
+ * @param FactoryInterface $connectionFactory Connection factory instance.
371
+ */
372
+ protected function discoverFromMaster(NodeConnectionInterface $connection, FactoryInterface $connectionFactory)
373
+ {
374
+ $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION'));
375
+ $replication = $this->handleInfoResponse($response);
376
+
377
+ if ($replication['role'] !== 'master') {
378
+ throw new ClientException("Role mismatch (expected master, got slave) [$connection]");
379
+ }
380
+
381
+ $this->slaves = array();
382
+
383
+ foreach ($replication as $k => $v) {
384
+ $parameters = null;
385
+
386
+ if (strpos($k, 'slave') === 0 && preg_match('/ip=(?P<host>.*),port=(?P<port>\d+)/', $v, $parameters)) {
387
+ $slaveConnection = $connectionFactory->create(array(
388
+ 'host' => $parameters['host'],
389
+ 'port' => $parameters['port'],
390
+ ));
391
+
392
+ $this->add($slaveConnection);
393
+ }
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Discovers the replication configuration by contacting one of the slaves.
399
+ *
400
+ * @param NodeConnectionInterface $connection Connection to one of the slaves.
401
+ * @param FactoryInterface $connectionFactory Connection factory instance.
402
+ */
403
+ protected function discoverFromSlave(NodeConnectionInterface $connection, FactoryInterface $connectionFactory)
404
+ {
405
+ $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION'));
406
+ $replication = $this->handleInfoResponse($response);
407
+
408
+ if ($replication['role'] !== 'slave') {
409
+ throw new ClientException("Role mismatch (expected slave, got master) [$connection]");
410
+ }
411
+
412
+ $masterConnection = $connectionFactory->create(array(
413
+ 'host' => $replication['master_host'],
414
+ 'port' => $replication['master_port'],
415
+ 'alias' => 'master',
416
+ ));
417
+
418
+ $this->add($masterConnection);
419
+
420
+ $this->discoverFromMaster($masterConnection, $connectionFactory);
421
+ }
422
+
423
+ /**
424
+ * Retries the execution of a command upon slave failure.
425
+ *
426
+ * @param CommandInterface $command Command instance.
427
+ * @param string $method Actual method.
428
+ *
429
+ * @return mixed
430
+ */
431
+ private function retryCommandOnFailure(CommandInterface $command, $method)
432
+ {
433
+ RETRY_COMMAND: {
434
+ try {
435
+ $connection = $this->getConnection($command);
436
+ $response = $connection->$method($command);
437
+
438
+ if ($response instanceof ResponseErrorInterface && $response->getErrorType() === 'LOADING') {
439
+ throw new ConnectionException($connection, "Redis is loading the dataset in memory [$connection]");
440
+ }
441
+ } catch (ConnectionException $exception) {
442
+ $connection = $exception->getConnection();
443
+ $connection->disconnect();
444
+
445
+ if ($connection === $this->master && !$this->autoDiscovery) {
446
+ // Throw immediately when master connection is failing, even
447
+ // when the command represents a read-only operation, unless
448
+ // automatic discovery has been enabled.
449
+ throw $exception;
450
+ } else {
451
+ // Otherwise remove the failing slave and attempt to execute
452
+ // the command again on one of the remaining slaves...
453
+ $this->remove($connection);
454
+ }
455
+
456
+ // ... that is, unless we have no more connections to use.
457
+ if (!$this->slaves && !$this->master) {
458
+ throw $exception;
459
+ } elseif ($this->autoDiscovery) {
460
+ $this->discover();
461
+ }
462
+
463
+ goto RETRY_COMMAND;
464
+ } catch (MissingMasterException $exception) {
465
+ if ($this->autoDiscovery) {
466
+ $this->discover();
467
+ } else {
468
+ throw $exception;
469
+ }
470
+
471
+ goto RETRY_COMMAND;
472
+ }
473
+ }
474
+
475
+ return $response;
476
+ }
477
+
478
+ /**
479
+ * {@inheritdoc}
480
+ */
481
+ public function writeRequest(CommandInterface $command)
482
+ {
483
+ $this->retryCommandOnFailure($command, __FUNCTION__);
484
+ }
485
+
486
+ /**
487
+ * {@inheritdoc}
488
+ */
489
+ public function readResponse(CommandInterface $command)
490
+ {
491
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
492
+ }
493
+
494
+ /**
495
+ * {@inheritdoc}
496
+ */
497
+ public function executeCommand(CommandInterface $command)
498
+ {
499
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
500
+ }
501
+
502
+ /**
503
+ * {@inheritdoc}
504
+ */
505
+ public function __sleep()
506
+ {
507
+ return array('master', 'slaves', 'strategy');
508
+ }
509
+ }
includes/predis/src/Connection/Aggregate/PredisCluster.php ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection\Aggregate;
13
+
14
+ use Predis\Cluster\PredisStrategy;
15
+ use Predis\Cluster\StrategyInterface;
16
+ use Predis\Command\CommandInterface;
17
+ use Predis\Connection\NodeConnectionInterface;
18
+ use Predis\NotSupportedException;
19
+
20
+ /**
21
+ * Abstraction for a cluster of aggregate connections to various Redis servers
22
+ * implementing client-side sharding based on pluggable distribution strategies.
23
+ *
24
+ * @author Daniele Alessandri <suppakilla@gmail.com>
25
+ *
26
+ * @todo Add the ability to remove connections from pool.
27
+ */
28
+ class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
29
+ {
30
+ private $pool;
31
+ private $strategy;
32
+ private $distributor;
33
+
34
+ /**
35
+ * @param StrategyInterface $strategy Optional cluster strategy.
36
+ */
37
+ public function __construct(StrategyInterface $strategy = null)
38
+ {
39
+ $this->pool = array();
40
+ $this->strategy = $strategy ?: new PredisStrategy();
41
+ $this->distributor = $this->strategy->getDistributor();
42
+ }
43
+
44
+ /**
45
+ * {@inheritdoc}
46
+ */
47
+ public function isConnected()
48
+ {
49
+ foreach ($this->pool as $connection) {
50
+ if ($connection->isConnected()) {
51
+ return true;
52
+ }
53
+ }
54
+
55
+ return false;
56
+ }
57
+
58
+ /**
59
+ * {@inheritdoc}
60
+ */
61
+ public function connect()
62
+ {
63
+ foreach ($this->pool as $connection) {
64
+ $connection->connect();
65
+ }
66
+ }
67
+
68
+ /**
69
+ * {@inheritdoc}
70
+ */
71
+ public function disconnect()
72
+ {
73
+ foreach ($this->pool as $connection) {
74
+ $connection->disconnect();
75
+ }
76
+ }
77
+
78
+ /**
79
+ * {@inheritdoc}
80
+ */
81
+ public function add(NodeConnectionInterface $connection)
82
+ {
83
+ $parameters = $connection->getParameters();
84
+
85
+ if (isset($parameters->alias)) {
86
+ $this->pool[$parameters->alias] = $connection;
87
+ } else {
88
+ $this->pool[] = $connection;
89
+ }
90
+
91
+ $weight = isset($parameters->weight) ? $parameters->weight : null;
92
+ $this->distributor->add($connection, $weight);
93
+ }
94
+
95
+ /**
96
+ * {@inheritdoc}
97
+ */
98
+ public function remove(NodeConnectionInterface $connection)
99
+ {
100
+ if (($id = array_search($connection, $this->pool, true)) !== false) {
101
+ unset($this->pool[$id]);
102
+ $this->distributor->remove($connection);
103
+
104
+ return true;
105
+ }
106
+
107
+ return false;
108
+ }
109
+
110
+ /**
111
+ * Removes a connection instance using its alias or index.
112
+ *
113
+ * @param string $connectionID Alias or index of a connection.
114
+ *
115
+ * @return bool Returns true if the connection was in the pool.
116
+ */
117
+ public function removeById($connectionID)
118
+ {
119
+ if ($connection = $this->getConnectionById($connectionID)) {
120
+ return $this->remove($connection);
121
+ }
122
+
123
+ return false;
124
+ }
125
+
126
+ /**
127
+ * {@inheritdoc}
128
+ */
129
+ public function getConnection(CommandInterface $command)
130
+ {
131
+ $slot = $this->strategy->getSlot($command);
132
+
133
+ if (!isset($slot)) {
134
+ throw new NotSupportedException(
135
+ "Cannot use '{$command->getId()}' over clusters of connections."
136
+ );
137
+ }
138
+
139
+ $node = $this->distributor->getBySlot($slot);
140
+
141
+ return $node;
142
+ }
143
+
144
+ /**
145
+ * {@inheritdoc}
146
+ */
147
+ public function getConnectionById($connectionID)
148
+ {
149
+ return isset($this->pool[$connectionID]) ? $this->pool[$connectionID] : null;
150
+ }
151
+
152
+ /**
153
+ * Retrieves a connection instance from the cluster using a key.
154
+ *
155
+ * @param string $key Key string.
156
+ *
157
+ * @return NodeConnectionInterface
158
+ */
159
+ public function getConnectionByKey($key)
160
+ {
161
+ $hash = $this->strategy->getSlotByKey($key);
162
+ $node = $this->distributor->getBySlot($hash);
163
+
164
+ return $node;
165
+ }
166
+
167
+ /**
168
+ * Returns the underlying command hash strategy used to hash commands by
169
+ * using keys found in their arguments.
170
+ *
171
+ * @return StrategyInterface
172
+ */
173
+ public function getClusterStrategy()
174
+ {
175
+ return $this->strategy;
176
+ }
177
+
178
+ /**
179
+ * {@inheritdoc}
180
+ */
181
+ public function count()
182
+ {
183
+ return count($this->pool);
184
+ }
185
+
186
+ /**
187
+ * {@inheritdoc}
188
+ */
189
+ public function getIterator()
190
+ {
191
+ return new \ArrayIterator($this->pool);
192
+ }
193
+
194
+ /**
195
+ * {@inheritdoc}
196
+ */
197
+ public function writeRequest(CommandInterface $command)
198
+ {
199
+ $this->getConnection($command)->writeRequest($command);
200
+ }
201
+
202
+ /**
203
+ * {@inheritdoc}
204
+ */
205
+ public function readResponse(CommandInterface $command)
206
+ {
207
+ return $this->getConnection($command)->readResponse($command);
208
+ }
209
+
210
+ /**
211
+ * {@inheritdoc}
212
+ */
213
+ public function executeCommand(CommandInterface $command)
214
+ {
215
+ return $this->getConnection($command)->executeCommand($command);
216
+ }
217
+
218
+ /**
219
+ * Executes the specified Redis command on all the nodes of a cluster.
220
+ *
221
+ * @param CommandInterface $command A Redis command.
222
+ *
223
+ * @return array
224
+ */
225
+ public function executeCommandOnNodes(CommandInterface $command)
226
+ {
227
+ $responses = array();
228
+
229
+ foreach ($this->pool as $connection) {
230
+ $responses[] = $connection->executeCommand($command);
231
+ }
232
+
233
+ return $responses;
234
+ }
235
+ }
includes/predis/src/Connection/Aggregate/RedisCluster.php ADDED
@@ -0,0 +1,673 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection\Aggregate;
13
+
14
+ use Predis\ClientException;
15
+ use Predis\Cluster\RedisStrategy as RedisClusterStrategy;
16
+ use Predis\Cluster\StrategyInterface;
17
+ use Predis\Command\CommandInterface;
18
+ use Predis\Command\RawCommand;
19
+ use Predis\Connection\ConnectionException;
20
+ use Predis\Connection\FactoryInterface;
21
+ use Predis\Connection\NodeConnectionInterface;
22
+ use Predis\NotSupportedException;
23
+ use Predis\Response\ErrorInterface as ErrorResponseInterface;
24
+
25
+ /**
26
+ * Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0).
27
+ *
28
+ * This connection backend offers smart support for redis-cluster by handling
29
+ * automatic slots map (re)generation upon -MOVED or -ASK responses returned by
30
+ * Redis when redirecting a client to a different node.
31
+ *
32
+ * The cluster can be pre-initialized using only a subset of the actual nodes in
33
+ * the cluster, Predis will do the rest by adjusting the slots map and creating
34
+ * the missing underlying connection instances on the fly.
35
+ *
36
+ * It is possible to pre-associate connections to a slots range with the "slots"
37
+ * parameter in the form "$first-$last". This can greatly reduce runtime node
38
+ * guessing and redirections.
39
+ *
40
+ * It is also possible to ask for the full and updated slots map directly to one
41
+ * of the nodes and optionally enable such a behaviour upon -MOVED redirections.
42
+ * Asking for the cluster configuration to Redis is actually done by issuing a
43
+ * CLUSTER SLOTS command to a random node in the pool.
44
+ *
45
+ * @author Daniele Alessandri <suppakilla@gmail.com>
46
+ */
47
+ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
48
+ {
49
+ private $useClusterSlots = true;
50
+ private $pool = array();
51
+ private $slots = array();
52
+ private $slotsMap;
53
+ private $strategy;
54
+ private $connections;
55
+ private $retryLimit = 5;
56
+
57
+ /**
58
+ * @param FactoryInterface $connections Optional connection factory.
59
+ * @param StrategyInterface $strategy Optional cluster strategy.
60
+ */
61
+ public function __construct(
62
+ FactoryInterface $connections,
63
+ StrategyInterface $strategy = null
64
+ ) {
65
+ $this->connections = $connections;
66
+ $this->strategy = $strategy ?: new RedisClusterStrategy();
67
+ }
68
+
69
+ /**
70
+ * Sets the maximum number of retries for commands upon server failure.
71
+ *
72
+ * -1 = unlimited retry attempts
73
+ * 0 = no retry attempts (fails immediatly)
74
+ * n = fail only after n retry attempts
75
+ *
76
+ * @param int $retry Number of retry attempts.
77
+ */
78
+ public function setRetryLimit($retry)
79
+ {
80
+ $this->retryLimit = (int) $retry;
81
+ }
82
+
83
+ /**
84
+ * {@inheritdoc}
85
+ */
86
+ public function isConnected()
87
+ {
88
+ foreach ($this->pool as $connection) {
89
+ if ($connection->isConnected()) {
90
+ return true;
91
+ }
92
+ }
93
+
94
+ return false;
95
+ }
96
+
97
+ /**
98
+ * {@inheritdoc}
99
+ */
100
+ public function connect()
101
+ {
102
+ if ($connection = $this->getRandomConnection()) {
103
+ $connection->connect();
104
+ }
105
+ }
106
+
107
+ /**
108
+ * {@inheritdoc}
109
+ */
110
+ public function disconnect()
111
+ {
112
+ foreach ($this->pool as $connection) {
113
+ $connection->disconnect();
114
+ }
115
+ }
116
+
117
+ /**
118
+ * {@inheritdoc}
119
+ */
120
+ public function add(NodeConnectionInterface $connection)
121
+ {
122
+ $this->pool[(string) $connection] = $connection;
123
+ unset($this->slotsMap);
124
+ }
125
+
126
+ /**
127
+ * {@inheritdoc}
128
+ */
129
+ public function remove(NodeConnectionInterface $connection)
130
+ {
131
+ if (false !== $id = array_search($connection, $this->pool, true)) {
132
+ unset(
133
+ $this->pool[$id],
134
+ $this->slotsMap
135
+ );
136
+
137
+ $this->slots = array_diff($this->slots, array($connection));
138
+
139
+ return true;
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ /**
146
+ * Removes a connection instance by using its identifier.
147
+ *
148
+ * @param string $connectionID Connection identifier.
149
+ *
150
+ * @return bool True if the connection was in the pool.
151
+ */
152
+ public function removeById($connectionID)
153
+ {
154
+ if (isset($this->pool[$connectionID])) {
155
+ unset(
156
+ $this->pool[$connectionID],
157
+ $this->slotsMap
158
+ );
159
+
160
+ return true;
161
+ }
162
+
163
+ return false;
164
+ }
165
+
166
+ /**
167
+ * Generates the current slots map by guessing the cluster configuration out
168
+ * of the connection parameters of the connections in the pool.
169
+ *
170
+ * Generation is based on the same algorithm used by Redis to generate the
171
+ * cluster, so it is most effective when all of the connections supplied on
172
+ * initialization have the "slots" parameter properly set accordingly to the
173
+ * current cluster configuration.
174
+ *
175
+ * @return array
176
+ */
177
+ public function buildSlotsMap()
178
+ {
179
+ $this->slotsMap = array();
180
+
181
+ foreach ($this->pool as $connectionID => $connection) {
182
+ $parameters = $connection->getParameters();
183
+
184
+ if (!isset($parameters->slots)) {
185
+ continue;
186
+ }
187
+
188
+ foreach (explode(',', $parameters->slots) as $slotRange) {
189
+ $slots = explode('-', $slotRange, 2);
190
+
191
+ if (!isset($slots[1])) {
192
+ $slots[1] = $slots[0];
193
+ }
194
+
195
+ $this->setSlots($slots[0], $slots[1], $connectionID);
196
+ }
197
+ }
198
+
199
+ return $this->slotsMap;
200
+ }
201
+
202
+ /**
203
+ * Queries the specified node of the cluster to fetch the updated slots map.
204
+ *
205
+ * When the connection fails, this method tries to execute the same command
206
+ * on a different connection picked at random from the pool of known nodes,
207
+ * up until the retry limit is reached.
208
+ *
209
+ * @param NodeConnectionInterface $connection Connection to a node of the cluster.
210
+ *
211
+ * @return mixed
212
+ */
213
+ private function queryClusterNodeForSlotsMap(NodeConnectionInterface $connection)
214
+ {
215
+ $retries = 0;
216
+ $command = RawCommand::create('CLUSTER', 'SLOTS');
217
+
218
+ RETRY_COMMAND: {
219
+ try {
220
+ $response = $connection->executeCommand($command);
221
+ } catch (ConnectionException $exception) {
222
+ $connection = $exception->getConnection();
223
+ $connection->disconnect();
224
+
225
+ $this->remove($connection);
226
+
227
+ if ($retries === $this->retryLimit) {
228
+ throw $exception;
229
+ }
230
+
231
+ if (!$connection = $this->getRandomConnection()) {
232
+ throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`');
233
+ }
234
+
235
+ ++$retries;
236
+ goto RETRY_COMMAND;
237
+ }
238
+ }
239
+
240
+ return $response;
241
+ }
242
+
243
+ /**
244
+ * Generates an updated slots map fetching the cluster configuration using
245
+ * the CLUSTER SLOTS command against the specified node or a random one from
246
+ * the pool.
247
+ *
248
+ * @param NodeConnectionInterface $connection Optional connection instance.
249
+ *
250
+ * @return array
251
+ */
252
+ public function askSlotsMap(NodeConnectionInterface $connection = null)
253
+ {
254
+ if (!$connection && !$connection = $this->getRandomConnection()) {
255
+ return array();
256
+ }
257
+
258
+ $this->resetSlotsMap();
259
+
260
+ $response = $this->queryClusterNodeForSlotsMap($connection);
261
+
262
+ foreach ($response as $slots) {
263
+ // We only support master servers for now, so we ignore subsequent
264
+ // elements in the $slots array identifying slaves.
265
+ list($start, $end, $master) = $slots;
266
+
267
+ if ($master[0] === '') {
268
+ $this->setSlots($start, $end, (string) $connection);
269
+ } else {
270
+ $this->setSlots($start, $end, "{$master[0]}:{$master[1]}");
271
+ }
272
+ }
273
+
274
+ return $this->slotsMap;
275
+ }
276
+
277
+ /**
278
+ * Resets the slots map cache.
279
+ */
280
+ public function resetSlotsMap()
281
+ {
282
+ $this->slotsMap = array();
283
+ }
284
+
285
+ /**
286
+ * Returns the current slots map for the cluster.
287
+ *
288
+ * The order of the returned $slot => $server dictionary is not guaranteed.
289
+ *
290
+ * @return array
291
+ */
292
+ public function getSlotsMap()
293
+ {
294
+ if (!isset($this->slotsMap)) {
295
+ $this->slotsMap = array();
296
+ }
297
+
298
+ return $this->slotsMap;
299
+ }
300
+
301
+ /**
302
+ * Pre-associates a connection to a slots range to avoid runtime guessing.
303
+ *
304
+ * @param int $first Initial slot of the range.
305
+ * @param int $last Last slot of the range.
306
+ * @param NodeConnectionInterface|string $connection ID or connection instance.
307
+ *
308
+ * @throws \OutOfBoundsException
309
+ */
310
+ public function setSlots($first, $last, $connection)
311
+ {
312
+ if ($first < 0x0000 || $first > 0x3FFF ||
313
+ $last < 0x0000 || $last > 0x3FFF ||
314
+ $last < $first
315
+ ) {
316
+ throw new \OutOfBoundsException(
317
+ "Invalid slot range for $connection: [$first-$last]."
318
+ );
319
+ }
320
+
321
+ $slots = array_fill($first, $last - $first + 1, (string) $connection);
322
+ $this->slotsMap = $this->getSlotsMap() + $slots;
323
+ }
324
+
325
+ /**
326
+ * Guesses the correct node associated to a given slot using a precalculated
327
+ * slots map, falling back to the same logic used by Redis to initialize a
328
+ * cluster (best-effort).
329
+ *
330
+ * @param int $slot Slot index.
331
+ *
332
+ * @return string Connection ID.
333
+ */
334
+ protected function guessNode($slot)
335
+ {
336
+ if (!$this->pool) {
337
+ throw new ClientException('No connections available in the pool');
338
+ }
339
+
340
+ if (!isset($this->slotsMap)) {
341
+ $this->buildSlotsMap();
342
+ }
343
+
344
+ if (isset($this->slotsMap[$slot])) {
345
+ return $this->slotsMap[$slot];
346
+ }
347
+
348
+ $count = count($this->pool);
349
+ $index = min((int) ($slot / (int) (16384 / $count)), $count - 1);
350
+ $nodes = array_keys($this->pool);
351
+
352
+ return $nodes[$index];
353
+ }
354
+
355
+ /**
356
+ * Creates a new connection instance from the given connection ID.
357
+ *
358
+ * @param string $connectionID Identifier for the connection.
359
+ *
360
+ * @return NodeConnectionInterface
361
+ */
362
+ protected function createConnection($connectionID)
363
+ {
364
+ $separator = strrpos($connectionID, ':');
365
+
366
+ return $this->connections->create(array(
367
+ 'host' => substr($connectionID, 0, $separator),
368
+ 'port' => substr($connectionID, $separator + 1),
369
+ ));
370
+ }
371
+
372
+ /**
373
+ * {@inheritdoc}
374
+ */
375
+ public function getConnection(CommandInterface $command)
376
+ {
377
+ $slot = $this->strategy->getSlot($command);
378
+
379
+ if (!isset($slot)) {
380
+ throw new NotSupportedException(
381
+ "Cannot use '{$command->getId()}' with redis-cluster."
382
+ );
383
+ }
384
+
385
+ if (isset($this->slots[$slot])) {
386
+ return $this->slots[$slot];
387
+ } else {
388
+ return $this->getConnectionBySlot($slot);
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Returns the connection currently associated to a given slot.
394
+ *
395
+ * @param int $slot Slot index.
396
+ *
397
+ * @throws \OutOfBoundsException
398
+ *
399
+ * @return NodeConnectionInterface
400
+ */
401
+ public function getConnectionBySlot($slot)
402
+ {
403
+ if ($slot < 0x0000 || $slot > 0x3FFF) {
404
+ throw new \OutOfBoundsException("Invalid slot [$slot].");
405
+ }
406
+
407
+ if (isset($this->slots[$slot])) {
408
+ return $this->slots[$slot];
409
+ }
410
+
411
+ $connectionID = $this->guessNode($slot);
412
+
413
+ if (!$connection = $this->getConnectionById($connectionID)) {
414
+ $connection = $this->createConnection($connectionID);
415
+ $this->pool[$connectionID] = $connection;
416
+ }
417
+
418
+ return $this->slots[$slot] = $connection;
419
+ }
420
+
421
+ /**
422
+ * {@inheritdoc}
423
+ */
424
+ public function getConnectionById($connectionID)
425
+ {
426
+ if (isset($this->pool[$connectionID])) {
427
+ return $this->pool[$connectionID];
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Returns a random connection from the pool.
433
+ *
434
+ * @return NodeConnectionInterface|null
435
+ */
436
+ protected function getRandomConnection()
437
+ {
438
+ if ($this->pool) {
439
+ return $this->pool[array_rand($this->pool)];
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Permanently associates the connection instance to a new slot.
445
+ * The connection is added to the connections pool if not yet included.
446
+ *
447
+ * @param NodeConnectionInterface $connection Connection instance.
448
+ * @param int $slot Target slot index.
449
+ */
450
+ protected function move(NodeConnectionInterface $connection, $slot)
451
+ {
452
+ $this->pool[(string) $connection] = $connection;
453
+ $this->slots[(int) $slot] = $connection;
454
+ }
455
+
456
+ /**
457
+ * Handles -ERR responses returned by Redis.
458
+ *
459
+ * @param CommandInterface $command Command that generated the -ERR response.
460
+ * @param ErrorResponseInterface $error Redis error response object.
461
+ *
462
+ * @return mixed
463
+ */
464
+ protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $error)
465
+ {
466
+ $details = explode(' ', $error->getMessage(), 2);
467
+
468
+ switch ($details[0]) {
469
+ case 'MOVED':
470
+ return $this->onMovedResponse($command, $details[1]);
471
+
472
+ case 'ASK':
473
+ return $this->onAskResponse($command, $details[1]);
474
+
475
+ default:
476
+ return $error;
477
+ }
478
+ }
479
+
480
+ /**
481
+ * Handles -MOVED responses by executing again the command against the node
482
+ * indicated by the Redis response.
483
+ *
484
+ * @param CommandInterface $command Command that generated the -MOVED response.
485
+ * @param string $details Parameters of the -MOVED response.
486
+ *
487
+ * @return mixed
488
+ */
489
+ protected function onMovedResponse(CommandInterface $command, $details)
490
+ {
491
+ list($slot, $connectionID) = explode(' ', $details, 2);
492
+
493
+ if (!$connection = $this->getConnectionById($connectionID)) {
494
+ $connection = $this->createConnection($connectionID);
495
+ }
496
+
497
+ if ($this->useClusterSlots) {
498
+ $this->askSlotsMap($connection);
499
+ }
500
+
501
+ $this->move($connection, $slot);
502
+ $response = $this->executeCommand($command);
503
+
504
+ return $response;
505
+ }
506
+
507
+ /**
508
+ * Handles -ASK responses by executing again the command against the node
509
+ * indicated by the Redis response.
510
+ *
511
+ * @param CommandInterface $command Command that generated the -ASK response.
512
+ * @param string $details Parameters of the -ASK response.
513
+ *
514
+ * @return mixed
515
+ */
516
+ protected function onAskResponse(CommandInterface $command, $details)
517
+ {
518
+ list($slot, $connectionID) = explode(' ', $details, 2);
519
+
520
+ if (!$connection = $this->getConnectionById($connectionID)) {
521
+ $connection = $this->createConnection($connectionID);
522
+ }
523
+
524
+ $connection->executeCommand(RawCommand::create('ASKING'));
525
+ $response = $connection->executeCommand($command);
526
+
527
+ return $response;
528
+ }
529
+
530
+ /**
531
+ * Ensures that a command is executed one more time on connection failure.
532
+ *
533
+ * The connection to the node that generated the error is evicted from the
534
+ * pool before trying to fetch an updated slots map from another node. If
535
+ * the new slots map points to an unreachable server the client gives up and
536
+ * throws the exception as the nodes participating in the cluster may still
537
+ * have to agree that something changed in the configuration of the cluster.
538
+ *
539
+ * @param CommandInterface $command Command instance.
540
+ * @param string $method Actual method.
541
+ *
542
+ * @return mixed
543
+ */
544
+ private function retryCommandOnFailure(CommandInterface $command, $method)
545
+ {
546
+ $failure = false;
547
+
548
+ RETRY_COMMAND: {
549
+ try {
550
+ $response = $this->getConnection($command)->$method($command);
551
+ } catch (ConnectionException $exception) {
552
+ $connection = $exception->getConnection();
553
+ $connection->disconnect();
554
+
555
+ $this->remove($connection);
556
+
557
+ if ($failure) {
558
+ throw $exception;
559
+ } elseif ($this->useClusterSlots) {
560
+ $this->askSlotsMap();
561
+ }
562
+
563
+ $failure = true;
564
+
565
+ goto RETRY_COMMAND;
566
+ }
567
+ }
568
+
569
+ return $response;
570
+ }
571
+
572
+ /**
573
+ * {@inheritdoc}
574
+ */
575
+ public function writeRequest(CommandInterface $command)
576
+ {
577
+ $this->retryCommandOnFailure($command, __FUNCTION__);
578
+ }
579
+
580
+ /**
581
+ * {@inheritdoc}
582
+ */
583
+ public function readResponse(CommandInterface $command)
584
+ {
585
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
586
+ }
587
+
588
+ /**
589
+ * {@inheritdoc}
590
+ */
591
+ public function executeCommand(CommandInterface $command)
592
+ {
593
+ $response = $this->retryCommandOnFailure($command, __FUNCTION__);
594
+
595
+ if ($response instanceof ErrorResponseInterface) {
596
+ return $this->onErrorResponse($command, $response);
597
+ }
598
+
599
+ return $response;
600
+ }
601
+
602
+ /**
603
+ * {@inheritdoc}
604
+ */
605
+ public function count()
606
+ {
607
+ return count($this->pool);
608
+ }
609
+
610
+ /**
611
+ * {@inheritdoc}
612
+ */
613
+ public function getIterator()
614
+ {
615
+ if ($this->useClusterSlots) {
616
+ $slotsmap = $this->getSlotsMap() ?: $this->askSlotsMap();
617
+ } else {
618
+ $slotsmap = $this->getSlotsMap() ?: $this->buildSlotsMap();
619
+ }
620
+
621
+ $connections = array();
622
+
623
+ foreach (array_unique($slotsmap) as $node) {
624
+ if (!$connection = $this->getConnectionById($node)) {
625
+ $this->add($connection = $this->createConnection($node));
626
+ }
627
+
628
+ $connections[] = $connection;
629
+ }
630
+
631
+ return new \ArrayIterator($connections);
632
+ }
633
+
634
+ /**
635
+ * Returns the underlying command hash strategy used to hash commands by
636
+ * using keys found in their arguments.
637
+ *
638
+ * @return StrategyInterface
639
+ */
640
+ public function getClusterStrategy()
641
+ {
642
+ return $this->strategy;
643
+ }
644
+
645
+ /**
646
+ * Returns the underlying connection factory used to create new connection
647
+ * instances to Redis nodes indicated by redis-cluster.
648
+ *
649
+ * @return FactoryInterface
650
+ */
651
+ public function getConnectionFactory()
652
+ {
653
+ return $this->connections;
654
+ }
655
+
656
+ /**
657
+ * Enables automatic fetching of the current slots map from one of the nodes
658
+ * using the CLUSTER SLOTS command. This option is enabled by default as
659
+ * asking the current slots map to Redis upon -MOVED responses may reduce
660
+ * overhead by eliminating the trial-and-error nature of the node guessing
661
+ * procedure, mostly when targeting many keys that would end up in a lot of
662
+ * redirections.
663
+ *
664
+ * The slots map can still be manually fetched using the askSlotsMap()
665
+ * method whether or not this option is enabled.
666
+ *
667
+ * @param bool $value Enable or disable the use of CLUSTER SLOTS.
668
+ */
669
+ public function useClusterSlots($value)
670
+ {
671
+ $this->useClusterSlots = (bool) $value;
672
+ }
673
+ }
includes/predis/src/Connection/Aggregate/ReplicationInterface.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection\Aggregate;
13
+
14
+ use Predis\Connection\AggregateConnectionInterface;
15
+ use Predis\Connection\NodeConnectionInterface;
16
+
17
+ /**
18
+ * Defines a group of Redis nodes in a master / slave replication setup.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ interface ReplicationInterface extends AggregateConnectionInterface
23
+ {
24
+ /**
25
+ * Switches the internal connection instance in use.
26
+ *
27
+ * @param string $connection Alias of a connection
28
+ */
29
+ public function switchTo($connection);
30
+
31
+ /**
32
+ * Returns the connection instance currently in use by the aggregate
33
+ * connection.
34
+ *
35
+ * @return NodeConnectionInterface
36
+ */
37
+ public function getCurrent();
38
+
39
+ /**
40
+ * Returns the connection instance for the master Redis node.
41
+ *
42
+ * @return NodeConnectionInterface
43
+ */
44
+ public function getMaster();
45
+
46
+ /**
47
+ * Returns a list of connection instances to slave nodes.
48
+ *
49
+ * @return NodeConnectionInterface
50
+ */
51
+ public function getSlaves();
52
+ }
includes/predis/src/Connection/Aggregate/SentinelReplication.php ADDED
@@ -0,0 +1,720 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection\Aggregate;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Command\RawCommand;
16
+ use Predis\CommunicationException;
17
+ use Predis\Connection\ConnectionException;
18
+ use Predis\Connection\FactoryInterface as ConnectionFactoryInterface;
19
+ use Predis\Connection\NodeConnectionInterface;
20
+ use Predis\Connection\Parameters;
21
+ use Predis\Replication\ReplicationStrategy;
22
+ use Predis\Replication\RoleException;
23
+ use Predis\Response\ErrorInterface as ErrorResponseInterface;
24
+ use Predis\Response\ServerException;
25
+
26
+ /**
27
+ * @author Daniele Alessandri <suppakilla@gmail.com>
28
+ * @author Ville Mattila <ville@eventio.fi>
29
+ */
30
+ class SentinelReplication implements ReplicationInterface
31
+ {
32
+ /**
33
+ * @var NodeConnectionInterface
34
+ */
35
+ protected $master;
36
+
37
+ /**
38
+ * @var NodeConnectionInterface[]
39
+ */
40
+ protected $slaves = array();
41
+
42
+ /**
43
+ * @var NodeConnectionInterface
44
+ */
45
+ protected $current;
46
+
47
+ /**
48
+ * @var string
49
+ */
50
+ protected $service;
51
+
52
+ /**
53
+ * @var ConnectionFactoryInterface
54
+ */
55
+ protected $connectionFactory;
56
+
57
+ /**
58
+ * @var ReplicationStrategy
59
+ */
60
+ protected $strategy;
61
+
62
+ /**
63
+ * @var NodeConnectionInterface[]
64
+ */
65
+ protected $sentinels = array();
66
+
67
+ /**
68
+ * @var NodeConnectionInterface
69
+ */
70
+ protected $sentinelConnection;
71
+
72
+ /**
73
+ * @var float
74
+ */
75
+ protected $sentinelTimeout = 0.100;
76
+
77
+ /**
78
+ * Max number of automatic retries of commands upon server failure.
79
+ *
80
+ * -1 = unlimited retry attempts
81
+ * 0 = no retry attempts (fails immediatly)
82
+ * n = fail only after n retry attempts
83
+ *
84
+ * @var int
85
+ */
86
+ protected $retryLimit = 20;
87
+
88
+ /**
89
+ * Time to wait in milliseconds before fetching a new configuration from one
90
+ * of the sentinel servers.
91
+ *
92
+ * @var int
93
+ */
94
+ protected $retryWait = 1000;
95
+
96
+ /**
97
+ * Flag for automatic fetching of available sentinels.
98
+ *
99
+ * @var bool
100
+ */
101
+ protected $updateSentinels = false;
102
+
103
+ /**
104
+ * @param string $service Name of the service for autodiscovery.
105
+ * @param array $sentinels Sentinel servers connection parameters.
106
+ * @param ConnectionFactoryInterface $connectionFactory Connection factory instance.
107
+ * @param ReplicationStrategy $strategy Replication strategy instance.
108
+ */
109
+ public function __construct(
110
+ $service,
111
+ array $sentinels,
112
+ ConnectionFactoryInterface $connectionFactory,
113
+ ReplicationStrategy $strategy = null
114
+ ) {
115
+ $this->sentinels = $sentinels;
116
+ $this->service = $service;
117
+ $this->connectionFactory = $connectionFactory;
118
+ $this->strategy = $strategy ?: new ReplicationStrategy();
119
+ }
120
+
121
+ /**
122
+ * Sets a default timeout for connections to sentinels.
123
+ *
124
+ * When "timeout" is present in the connection parameters of sentinels, its
125
+ * value overrides the default sentinel timeout.
126
+ *
127
+ * @param float $timeout Timeout value.
128
+ */
129
+ public function setSentinelTimeout($timeout)
130
+ {
131
+ $this->sentinelTimeout = (float) $timeout;
132
+ }
133
+
134
+ /**
135
+ * Sets the maximum number of retries for commands upon server failure.
136
+ *
137
+ * -1 = unlimited retry attempts
138
+ * 0 = no retry attempts (fails immediatly)
139
+ * n = fail only after n retry attempts
140
+ *
141
+ * @param int $retry Number of retry attempts.
142
+ */
143
+ public function setRetryLimit($retry)
144
+ {
145
+ $this->retryLimit = (int) $retry;
146
+ }
147
+
148
+ /**
149
+ * Sets the time to wait (in seconds) before fetching a new configuration
150
+ * from one of the sentinels.
151
+ *
152
+ * @param float $seconds Time to wait before the next attempt.
153
+ */
154
+ public function setRetryWait($seconds)
155
+ {
156
+ $this->retryWait = (float) $seconds;
157
+ }
158
+
159
+ /**
160
+ * Set automatic fetching of available sentinels.
161
+ *
162
+ * @param bool $update Enable or disable automatic updates.
163
+ */
164
+ public function setUpdateSentinels($update)
165
+ {
166
+ $this->updateSentinels = (bool) $update;
167
+ }
168
+
169
+ /**
170
+ * Resets the current connection.
171
+ */
172
+ protected function reset()
173
+ {
174
+ $this->current = null;
175
+ }
176
+
177
+ /**
178
+ * Wipes the current list of master and slaves nodes.
179
+ */
180
+ protected function wipeServerList()
181
+ {
182
+ $this->reset();
183
+
184
+ $this->master = null;
185
+ $this->slaves = array();
186
+ }
187
+
188
+ /**
189
+ * {@inheritdoc}
190
+ */
191
+ public function add(NodeConnectionInterface $connection)
192
+ {
193
+ $alias = $connection->getParameters()->alias;
194
+
195
+ if ($alias === 'master') {
196
+ $this->master = $connection;
197
+ } else {
198
+ $this->slaves[$alias ?: count($this->slaves)] = $connection;
199
+ }
200
+
201
+ $this->reset();
202
+ }
203
+
204
+ /**
205
+ * {@inheritdoc}
206
+ */
207
+ public function remove(NodeConnectionInterface $connection)
208
+ {
209
+ if ($connection === $this->master) {
210
+ $this->master = null;
211
+ $this->reset();
212
+
213
+ return true;
214
+ }
215
+
216
+ if (false !== $id = array_search($connection, $this->slaves, true)) {
217
+ unset($this->slaves[$id]);
218
+ $this->reset();
219
+
220
+ return true;
221
+ }
222
+
223
+ return false;
224
+ }
225
+
226
+ /**
227
+ * Creates a new connection to a sentinel server.
228
+ *
229
+ * @return NodeConnectionInterface
230
+ */
231
+ protected function createSentinelConnection($parameters)
232
+ {
233
+ if ($parameters instanceof NodeConnectionInterface) {
234
+ return $parameters;
235
+ }
236
+
237
+ if (is_string($parameters)) {
238
+ $parameters = Parameters::parse($parameters);
239
+ }
240
+
241
+ if (is_array($parameters)) {
242
+ // We explicitly set "database" and "password" to null,
243
+ // so that no AUTH and SELECT command is send to the sentinels.
244
+ $parameters['database'] = null;
245
+ $parameters['password'] = null;
246
+
247
+ if (!isset($parameters['timeout'])) {
248
+ $parameters['timeout'] = $this->sentinelTimeout;
249
+ }
250
+ }
251
+
252
+ $connection = $this->connectionFactory->create($parameters);
253
+
254
+ return $connection;
255
+ }
256
+
257
+ /**
258
+ * Returns the current sentinel connection.
259
+ *
260
+ * If there is no active sentinel connection, a new connection is created.
261
+ *
262
+ * @return NodeConnectionInterface
263
+ */
264
+ public function getSentinelConnection()
265
+ {
266
+ if (!$this->sentinelConnection) {
267
+ if (!$this->sentinels) {
268
+ throw new \Predis\ClientException('No sentinel server available for autodiscovery.');
269
+ }
270
+
271
+ $sentinel = array_shift($this->sentinels);
272
+ $this->sentinelConnection = $this->createSentinelConnection($sentinel);
273
+ }
274
+
275
+ return $this->sentinelConnection;
276
+ }
277
+
278
+ /**
279
+ * Fetches an updated list of sentinels from a sentinel.
280
+ */
281
+ public function updateSentinels()
282
+ {
283
+ SENTINEL_QUERY: {
284
+ $sentinel = $this->getSentinelConnection();
285
+
286
+ try {
287
+ $payload = $sentinel->executeCommand(
288
+ RawCommand::create('SENTINEL', 'sentinels', $this->service)
289
+ );
290
+
291
+ $this->sentinels = array();
292
+ // NOTE: sentinel server does not return itself, so we add it back.
293
+ $this->sentinels[] = $sentinel->getParameters()->toArray();
294
+
295
+ foreach ($payload as $sentinel) {
296
+ $this->sentinels[] = array(
297
+ 'host' => $sentinel[3],
298
+ 'port' => $sentinel[5],
299
+ );
300
+ }
301
+ } catch (ConnectionException $exception) {
302
+ $this->sentinelConnection = null;
303
+
304
+ goto SENTINEL_QUERY;
305
+ }
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Fetches the details for the master and slave servers from a sentinel.
311
+ */
312
+ public function querySentinel()
313
+ {
314
+ $this->wipeServerList();
315
+
316
+ $this->updateSentinels();
317
+ $this->getMaster();
318
+ $this->getSlaves();
319
+ }
320
+
321
+ /**
322
+ * Handles error responses returned by redis-sentinel.
323
+ *
324
+ * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
325
+ * @param ErrorResponseInterface $error Error response.
326
+ */
327
+ private function handleSentinelErrorResponse(NodeConnectionInterface $sentinel, ErrorResponseInterface $error)
328
+ {
329
+ if ($error->getErrorType() === 'IDONTKNOW') {
330
+ throw new ConnectionException($sentinel, $error->getMessage());
331
+ } else {
332
+ throw new ServerException($error->getMessage());
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Fetches the details for the master server from a sentinel.
338
+ *
339
+ * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
340
+ * @param string $service Name of the service.
341
+ *
342
+ * @return array
343
+ */
344
+ protected function querySentinelForMaster(NodeConnectionInterface $sentinel, $service)
345
+ {
346
+ $payload = $sentinel->executeCommand(
347
+ RawCommand::create('SENTINEL', 'get-master-addr-by-name', $service)
348
+ );
349
+
350
+ if ($payload === null) {
351
+ throw new ServerException('ERR No such master with that name');
352
+ }
353
+
354
+ if ($payload instanceof ErrorResponseInterface) {
355
+ $this->handleSentinelErrorResponse($sentinel, $payload);
356
+ }
357
+
358
+ return array(
359
+ 'host' => $payload[0],
360
+ 'port' => $payload[1],
361
+ 'alias' => 'master',
362
+ );
363
+ }
364
+
365
+ /**
366
+ * Fetches the details for the slave servers from a sentinel.
367
+ *
368
+ * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
369
+ * @param string $service Name of the service.
370
+ *
371
+ * @return array
372
+ */
373
+ protected function querySentinelForSlaves(NodeConnectionInterface $sentinel, $service)
374
+ {
375
+ $slaves = array();
376
+
377
+ $payload = $sentinel->executeCommand(
378
+ RawCommand::create('SENTINEL', 'slaves', $service)
379
+ );
380
+
381
+ if ($payload instanceof ErrorResponseInterface) {
382
+ $this->handleSentinelErrorResponse($sentinel, $payload);
383
+ }
384
+
385
+ foreach ($payload as $slave) {
386
+ $flags = explode(',', $slave[9]);
387
+
388
+ if (array_intersect($flags, array('s_down', 'o_down', 'disconnected'))) {
389
+ continue;
390
+ }
391
+
392
+ $slaves[] = array(
393
+ 'host' => $slave[3],
394
+ 'port' => $slave[5],
395
+ 'alias' => "slave-$slave[1]",
396
+ );
397
+ }
398
+
399
+ return $slaves;
400
+ }
401
+
402
+ /**
403
+ * {@inheritdoc}
404
+ */
405
+ public function getCurrent()
406
+ {
407
+ return $this->current;
408
+ }
409
+
410
+ /**
411
+ * {@inheritdoc}
412
+ */
413
+ public function getMaster()
414
+ {
415
+ if ($this->master) {
416
+ return $this->master;
417
+ }
418
+
419
+ if ($this->updateSentinels) {
420
+ $this->updateSentinels();
421
+ }
422
+
423
+ SENTINEL_QUERY: {
424
+ $sentinel = $this->getSentinelConnection();
425
+
426
+ try {
427
+ $masterParameters = $this->querySentinelForMaster($sentinel, $this->service);
428
+ $masterConnection = $this->connectionFactory->create($masterParameters);
429
+
430
+ $this->add($masterConnection);
431
+ } catch (ConnectionException $exception) {
432
+ $this->sentinelConnection = null;
433
+
434
+ goto SENTINEL_QUERY;
435
+ }
436
+ }
437
+
438
+ return $masterConnection;
439
+ }
440
+
441
+ /**
442
+ * {@inheritdoc}
443
+ */
444
+ public function getSlaves()
445
+ {
446
+ if ($this->slaves) {
447
+ return array_values($this->slaves);
448
+ }
449
+
450
+ if ($this->updateSentinels) {
451
+ $this->updateSentinels();
452
+ }
453
+
454
+ SENTINEL_QUERY: {
455
+ $sentinel = $this->getSentinelConnection();
456
+
457
+ try {
458
+ $slavesParameters = $this->querySentinelForSlaves($sentinel, $this->service);
459
+
460
+ foreach ($slavesParameters as $slaveParameters) {
461
+ $this->add($this->connectionFactory->create($slaveParameters));
462
+ }
463
+ } catch (ConnectionException $exception) {
464
+ $this->sentinelConnection = null;
465
+
466
+ goto SENTINEL_QUERY;
467
+ }
468
+ }
469
+
470
+ return array_values($this->slaves ?: array());
471
+ }
472
+
473
+ /**
474
+ * Returns a random slave.
475
+ *
476
+ * @return NodeConnectionInterface
477
+ */
478
+ protected function pickSlave()
479
+ {
480
+ if ($slaves = $this->getSlaves()) {
481
+ return $slaves[rand(1, count($slaves)) - 1];
482
+ }
483
+ }
484
+
485
+ /**
486
+ * Returns the connection instance in charge for the given command.
487
+ *
488
+ * @param CommandInterface $command Command instance.
489
+ *
490
+ * @return NodeConnectionInterface
491
+ */
492
+ private function getConnectionInternal(CommandInterface $command)
493
+ {
494
+ if (!$this->current) {
495
+ if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) {
496
+ $this->current = $slave;
497
+ } else {
498
+ $this->current = $this->getMaster();
499
+ }
500
+
501
+ return $this->current;
502
+ }
503
+
504
+ if ($this->current === $this->master) {
505
+ return $this->current;
506
+ }
507
+
508
+ if (!$this->strategy->isReadOperation($command)) {
509
+ $this->current = $this->getMaster();
510
+ }
511
+
512
+ return $this->current;
513
+ }
514
+
515
+ /**
516
+ * Asserts that the specified connection matches an expected role.
517
+ *
518
+ * @param NodeConnectionInterface $sentinel Connection to a redis server.
519
+ * @param string $role Expected role of the server ("master", "slave" or "sentinel").
520
+ */
521
+ protected function assertConnectionRole(NodeConnectionInterface $connection, $role)
522
+ {
523
+ $role = strtolower($role);
524
+ $actualRole = $connection->executeCommand(RawCommand::create('ROLE'));
525
+
526
+ if ($role !== $actualRole[0]) {
527
+ throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]");
528
+ }
529
+ }
530
+
531
+ /**
532
+ * {@inheritdoc}
533
+ */
534
+ public function getConnection(CommandInterface $command)
535
+ {
536
+ $connection = $this->getConnectionInternal($command);
537
+
538
+ if (!$connection->isConnected()) {
539
+ // When we do not have any available slave in the pool we can expect
540
+ // read-only operations to hit the master server.
541
+ $expectedRole = $this->strategy->isReadOperation($command) && $this->slaves ? 'slave' : 'master';
542
+ $this->assertConnectionRole($connection, $expectedRole);
543
+ }
544
+
545
+ return $connection;
546
+ }
547
+
548
+ /**
549
+ * {@inheritdoc}
550
+ */
551
+ public function getConnectionById($connectionId)
552
+ {
553
+ if ($connectionId === 'master') {
554
+ return $this->getMaster();
555
+ }
556
+
557
+ $this->getSlaves();
558
+
559
+ if (isset($this->slaves[$connectionId])) {
560
+ return $this->slaves[$connectionId];
561
+ }
562
+ }
563
+
564
+ /**
565
+ * {@inheritdoc}
566
+ */
567
+ public function switchTo($connection)
568
+ {
569
+ if (!$connection instanceof NodeConnectionInterface) {
570
+ $connection = $this->getConnectionById($connection);
571
+ }
572
+
573
+ if ($connection && $connection === $this->current) {
574
+ return;
575
+ }
576
+
577
+ if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) {
578
+ throw new \InvalidArgumentException('Invalid connection or connection not found.');
579
+ }
580
+
581
+ $connection->connect();
582
+
583
+ if ($this->current) {
584
+ $this->current->disconnect();
585
+ }
586
+
587
+ $this->current = $connection;
588
+ }
589
+
590
+ /**
591
+ * Switches to the master server.
592
+ */
593
+ public function switchToMaster()
594
+ {
595
+ $this->switchTo('master');
596
+ }
597
+
598
+ /**
599
+ * Switches to a random slave server.
600
+ */
601
+ public function switchToSlave()
602
+ {
603
+ $connection = $this->pickSlave();
604
+ $this->switchTo($connection);
605
+ }
606
+
607
+ /**
608
+ * {@inheritdoc}
609
+ */
610
+ public function isConnected()
611
+ {
612
+ return $this->current ? $this->current->isConnected() : false;
613
+ }
614
+
615
+ /**
616
+ * {@inheritdoc}
617
+ */
618
+ public function connect()
619
+ {
620
+ if (!$this->current) {
621
+ if (!$this->current = $this->pickSlave()) {
622
+ $this->current = $this->getMaster();
623
+ }
624
+ }
625
+
626
+ $this->current->connect();
627
+ }
628
+
629
+ /**
630
+ * {@inheritdoc}
631
+ */
632
+ public function disconnect()
633
+ {
634
+ if ($this->master) {
635
+ $this->master->disconnect();
636
+ }
637
+
638
+ foreach ($this->slaves as $connection) {
639
+ $connection->disconnect();
640
+ }
641
+ }
642
+
643
+ /**
644
+ * Retries the execution of a command upon server failure after asking a new
645
+ * configuration to one of the sentinels.
646
+ *
647
+ * @param CommandInterface $command Command instance.
648
+ * @param string $method Actual method.
649
+ *
650
+ * @return mixed
651
+ */
652
+ private function retryCommandOnFailure(CommandInterface $command, $method)
653
+ {
654
+ $retries = 0;
655
+
656
+ SENTINEL_RETRY: {
657
+ try {
658
+ $response = $this->getConnection($command)->$method($command);
659
+ } catch (CommunicationException $exception) {
660
+ $this->wipeServerList();
661
+ $exception->getConnection()->disconnect();
662
+
663
+ if ($retries == $this->retryLimit) {
664
+ throw $exception;
665
+ }
666
+
667
+ usleep($this->retryWait * 1000);
668
+
669
+ ++$retries;
670
+ goto SENTINEL_RETRY;
671
+ }
672
+ }
673
+
674
+ return $response;
675
+ }
676
+
677
+ /**
678
+ * {@inheritdoc}
679
+ */
680
+ public function writeRequest(CommandInterface $command)
681
+ {
682
+ $this->retryCommandOnFailure($command, __FUNCTION__);
683
+ }
684
+
685
+ /**
686
+ * {@inheritdoc}
687
+ */
688
+ public function readResponse(CommandInterface $command)
689
+ {
690
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
691
+ }
692
+
693
+ /**
694
+ * {@inheritdoc}
695
+ */
696
+ public function executeCommand(CommandInterface $command)
697
+ {
698
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
699
+ }
700
+
701
+ /**
702
+ * Returns the underlying replication strategy.
703
+ *
704
+ * @return ReplicationStrategy
705
+ */
706
+ public function getReplicationStrategy()
707
+ {
708
+ return $this->strategy;
709
+ }
710
+
711
+ /**
712
+ * {@inheritdoc}
713
+ */
714
+ public function __sleep()
715
+ {
716
+ return array(
717
+ 'master', 'slaves', 'service', 'sentinels', 'connectionFactory', 'strategy',
718
+ );
719
+ }
720
+ }
includes/predis/src/Connection/AggregateConnectionInterface.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * Defines a virtual connection composed of multiple connection instances to
18
+ * single Redis nodes.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ interface AggregateConnectionInterface extends ConnectionInterface
23
+ {
24
+ /**
25
+ * Adds a connection instance to the aggregate connection.
26
+ *
27
+ * @param NodeConnectionInterface $connection Connection instance.
28
+ */
29
+ public function add(NodeConnectionInterface $connection);
30
+
31
+ /**
32
+ * Removes the specified connection instance from the aggregate connection.
33
+ *
34
+ * @param NodeConnectionInterface $connection Connection instance.
35
+ *
36
+ * @return bool Returns true if the connection was in the pool.
37
+ */
38
+ public function remove(NodeConnectionInterface $connection);
39
+
40
+ /**
41
+ * Returns the connection instance in charge for the given command.
42
+ *
43
+ * @param CommandInterface $command Command instance.
44
+ *
45
+ * @return NodeConnectionInterface
46
+ */
47
+ public function getConnection(CommandInterface $command);
48
+
49
+ /**
50
+ * Returns a connection instance from the aggregate connection by its alias.
51
+ *
52
+ * @param string $connectionID Connection alias.
53
+ *
54
+ * @return NodeConnectionInterface|null
55
+ */
56
+ public function getConnectionById($connectionID);
57
+ }
includes/predis/src/Connection/CompositeConnectionInterface.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ /**
15
+ * Defines a connection to communicate with a single Redis server that leverages
16
+ * an external protocol processor to handle pluggable protocol handlers.
17
+ *
18
+ * @author Daniele Alessandri <suppakilla@gmail.com>
19
+ */
20
+ interface CompositeConnectionInterface extends NodeConnectionInterface
21
+ {
22
+ /**
23
+ * Returns the protocol processor used by the connection.
24
+ */
25
+ public function getProtocol();
26
+
27
+ /**
28
+ * Writes the buffer containing over the connection.
29
+ *
30
+ * @param string $buffer String buffer to be sent over the connection.
31
+ */
32
+ public function writeBuffer($buffer);
33
+
34
+ /**
35
+ * Reads the given number of bytes from the connection.
36
+ *
37
+ * @param int $length Number of bytes to read from the connection.
38
+ *
39
+ * @return string
40
+ */
41
+ public function readBuffer($length);
42
+
43
+ /**
44
+ * Reads a line from the connection.
45
+ *
46
+ * @param string
47
+ */
48
+ public function readLine();
49
+ }
includes/predis/src/Connection/CompositeStreamConnection.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Protocol\ProtocolProcessorInterface;
16
+ use Predis\Protocol\Text\ProtocolProcessor as TextProtocolProcessor;
17
+
18
+ /**
19
+ * Connection abstraction to Redis servers based on PHP's stream that uses an
20
+ * external protocol processor defining the protocol used for the communication.
21
+ *
22
+ * @author Daniele Alessandri <suppakilla@gmail.com>
23
+ */
24
+ class CompositeStreamConnection extends StreamConnection implements CompositeConnectionInterface
25
+ {
26
+ protected $protocol;
27
+
28
+ /**
29
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
30
+ * @param ProtocolProcessorInterface $protocol Protocol processor.
31
+ */
32
+ public function __construct(
33
+ ParametersInterface $parameters,
34
+ ProtocolProcessorInterface $protocol = null
35
+ ) {
36
+ $this->parameters = $this->assertParameters($parameters);
37
+ $this->protocol = $protocol ?: new TextProtocolProcessor();
38
+ }
39
+
40
+ /**
41
+ * {@inheritdoc}
42
+ */
43
+ public function getProtocol()
44
+ {
45
+ return $this->protocol;
46
+ }
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ public function writeBuffer($buffer)
52
+ {
53
+ $this->write($buffer);
54
+ }
55
+
56
+ /**
57
+ * {@inheritdoc}
58
+ */
59
+ public function readBuffer($length)
60
+ {
61
+ if ($length <= 0) {
62
+ throw new \InvalidArgumentException('Length parameter must be greater than 0.');
63
+ }
64
+
65
+ $value = '';
66
+ $socket = $this->getResource();
67
+
68
+ do {
69
+ $chunk = fread($socket, $length);
70
+
71
+ if ($chunk === false || $chunk === '') {
72
+ $this->onConnectionError('Error while reading bytes from the server.');
73
+ }
74
+
75
+ $value .= $chunk;
76
+ } while (($length -= strlen($chunk)) > 0);
77
+
78
+ return $value;
79
+ }
80
+
81
+ /**
82
+ * {@inheritdoc}
83
+ */
84
+ public function readLine()
85
+ {
86
+ $value = '';
87
+ $socket = $this->getResource();
88
+
89
+ do {
90
+ $chunk = fgets($socket);
91
+
92
+ if ($chunk === false || $chunk === '') {
93
+ $this->onConnectionError('Error while reading line from the server.');
94
+ }
95
+
96
+ $value .= $chunk;
97
+ } while (substr($value, -2) !== "\r\n");
98
+
99
+ return substr($value, 0, -2);
100
+ }
101
+
102
+ /**
103
+ * {@inheritdoc}
104
+ */
105
+ public function writeRequest(CommandInterface $command)
106
+ {
107
+ $this->protocol->write($this, $command);
108
+ }
109
+
110
+ /**
111
+ * {@inheritdoc}
112
+ */
113
+ public function read()
114
+ {
115
+ return $this->protocol->read($this);
116
+ }
117
+
118
+ /**
119
+ * {@inheritdoc}
120
+ */
121
+ public function __sleep()
122
+ {
123
+ return array_merge(parent::__sleep(), array('protocol'));
124
+ }
125
+ }
includes/predis/src/Connection/ConnectionException.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\CommunicationException;
15
+
16
+ /**
17
+ * Exception class that identifies connection-related errors.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ class ConnectionException extends CommunicationException
22
+ {
23
+ }
includes/predis/src/Connection/ConnectionInterface.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * Defines a connection object used to communicate with one or multiple
18
+ * Redis servers.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ interface ConnectionInterface
23
+ {
24
+ /**
25
+ * Opens the connection to Redis.
26
+ */
27
+ public function connect();
28
+
29
+ /**
30
+ * Closes the connection to Redis.
31
+ */
32
+ public function disconnect();
33
+
34
+ /**
35
+ * Checks if the connection to Redis is considered open.
36
+ *
37
+ * @return bool
38
+ */
39
+ public function isConnected();
40
+
41
+ /**
42
+ * Writes the request for the given command over the connection.
43
+ *
44
+ * @param CommandInterface $command Command instance.
45
+ */
46
+ public function writeRequest(CommandInterface $command);
47
+
48
+ /**
49
+ * Reads the response to the given command from the connection.
50
+ *
51
+ * @param CommandInterface $command Command instance.
52
+ *
53
+ * @return mixed
54
+ */
55
+ public function readResponse(CommandInterface $command);
56
+
57
+ /**
58
+ * Writes a request for the given command over the connection and reads back
59
+ * the response returned by Redis.
60
+ *
61
+ * @param CommandInterface $command Command instance.
62
+ *
63
+ * @return mixed
64
+ */
65
+ public function executeCommand(CommandInterface $command);
66
+ }
includes/predis/src/Connection/Factory.php ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\RawCommand;
15
+
16
+ /**
17
+ * Standard connection factory for creating connections to Redis nodes.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ class Factory implements FactoryInterface
22
+ {
23
+ private $defaults = array();
24
+
25
+ protected $schemes = array(
26
+ 'tcp' => 'Predis\Connection\StreamConnection',
27
+ 'unix' => 'Predis\Connection\StreamConnection',
28
+ 'tls' => 'Predis\Connection\StreamConnection',
29
+ 'redis' => 'Predis\Connection\StreamConnection',
30
+ 'rediss' => 'Predis\Connection\StreamConnection',
31
+ 'http' => 'Predis\Connection\WebdisConnection',
32
+ );
33
+
34
+ /**
35
+ * Checks if the provided argument represents a valid connection class
36
+ * implementing Predis\Connection\NodeConnectionInterface. Optionally,
37
+ * callable objects are used for lazy initialization of connection objects.
38
+ *
39
+ * @param mixed $initializer FQN of a connection class or a callable for lazy initialization.
40
+ *
41
+ * @throws \InvalidArgumentException
42
+ *
43
+ * @return mixed
44
+ */
45
+ protected function checkInitializer($initializer)
46
+ {
47
+ if (is_callable($initializer)) {
48
+ return $initializer;
49
+ }
50
+
51
+ $class = new \ReflectionClass($initializer);
52
+
53
+ if (!$class->isSubclassOf('Predis\Connection\NodeConnectionInterface')) {
54
+ throw new \InvalidArgumentException(
55
+ 'A connection initializer must be a valid connection class or a callable object.'
56
+ );
57
+ }
58
+
59
+ return $initializer;
60
+ }
61
+
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ public function define($scheme, $initializer)
66
+ {
67
+ $this->schemes[$scheme] = $this->checkInitializer($initializer);
68
+ }
69
+
70
+ /**
71
+ * {@inheritdoc}
72
+ */
73
+ public function undefine($scheme)
74
+ {
75
+ unset($this->schemes[$scheme]);
76
+ }
77
+
78
+ /**
79
+ * {@inheritdoc}
80
+ */
81
+ public function create($parameters)
82
+ {
83
+ if (!$parameters instanceof ParametersInterface) {
84
+ $parameters = $this->createParameters($parameters);
85
+ }
86
+
87
+ $scheme = $parameters->scheme;
88
+
89
+ if (!isset($this->schemes[$scheme])) {
90
+ throw new \InvalidArgumentException("Unknown connection scheme: '$scheme'.");
91
+ }
92
+
93
+ $initializer = $this->schemes[$scheme];
94
+
95
+ if (is_callable($initializer)) {
96
+ $connection = call_user_func($initializer, $parameters, $this);
97
+ } else {
98
+ $connection = new $initializer($parameters);
99
+ $this->prepareConnection($connection);
100
+ }
101
+
102
+ if (!$connection instanceof NodeConnectionInterface) {
103
+ throw new \UnexpectedValueException(
104
+ 'Objects returned by connection initializers must implement '.
105
+ "'Predis\Connection\NodeConnectionInterface'."
106
+ );
107
+ }
108
+
109
+ return $connection;
110
+ }
111
+
112
+ /**
113
+ * {@inheritdoc}
114
+ */
115
+ public function aggregate(AggregateConnectionInterface $connection, array $parameters)
116
+ {
117
+ foreach ($parameters as $node) {
118
+ $connection->add($node instanceof NodeConnectionInterface ? $node : $this->create($node));
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Assigns a default set of parameters applied to new connections.
124
+ *
125
+ * The set of parameters passed to create a new connection have precedence
126
+ * over the default values set for the connection factory.
127
+ *
128
+ * @param array $parameters Set of connection parameters.
129
+ */
130
+ public function setDefaultParameters(array $parameters)
131
+ {
132
+ $this->defaults = $parameters;
133
+ }
134
+
135
+ /**
136
+ * Returns the default set of parameters applied to new connections.
137
+ *
138
+ * @return array
139
+ */
140
+ public function getDefaultParameters()
141
+ {
142
+ return $this->defaults;
143
+ }
144
+
145
+ /**
146
+ * Creates a connection parameters instance from the supplied argument.
147
+ *
148
+ * @param mixed $parameters Original connection parameters.
149
+ *
150
+ * @return ParametersInterface
151
+ */
152
+ protected function createParameters($parameters)
153
+ {
154
+ if (is_string($parameters)) {
155
+ $parameters = Parameters::parse($parameters);
156
+ } else {
157
+ $parameters = $parameters ?: array();
158
+ }
159
+
160
+ if ($this->defaults) {
161
+ $parameters += $this->defaults;
162
+ }
163
+
164
+ return new Parameters($parameters);
165
+ }
166
+
167
+ /**
168
+ * Prepares a connection instance after its initialization.
169
+ *
170
+ * @param NodeConnectionInterface $connection Connection instance.
171
+ */
172
+ protected function prepareConnection(NodeConnectionInterface $connection)
173
+ {
174
+ $parameters = $connection->getParameters();
175
+
176
+ if (isset($parameters->password)) {
177
+ $connection->addConnectCommand(
178
+ new RawCommand(array('AUTH', $parameters->password))
179
+ );
180
+ }
181
+
182
+ if (isset($parameters->database)) {
183
+ $connection->addConnectCommand(
184
+ new RawCommand(array('SELECT', $parameters->database))
185
+ );
186
+ }
187
+ }
188
+ }
includes/predis/src/Connection/FactoryInterface.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ /**
15
+ * Interface for classes providing a factory of connections to Redis nodes.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ interface FactoryInterface
20
+ {
21
+ /**
22
+ * Defines or overrides the connection class identified by a scheme prefix.
23
+ *
24
+ * @param string $scheme Target connection scheme.
25
+ * @param mixed $initializer Fully-qualified name of a class or a callable for lazy initialization.
26
+ */
27
+ public function define($scheme, $initializer);
28
+
29
+ /**
30
+ * Undefines the connection identified by a scheme prefix.
31
+ *
32
+ * @param string $scheme Target connection scheme.
33
+ */
34
+ public function undefine($scheme);
35
+
36
+ /**
37
+ * Creates a new connection object.
38
+ *
39
+ * @param mixed $parameters Initialization parameters for the connection.
40
+ *
41
+ * @return NodeConnectionInterface
42
+ */
43
+ public function create($parameters);
44
+
45
+ /**
46
+ * Aggregates single connections into an aggregate connection instance.
47
+ *
48
+ * @param AggregateConnectionInterface $aggregate Aggregate connection instance.
49
+ * @param array $parameters List of parameters for each connection.
50
+ */
51
+ public function aggregate(AggregateConnectionInterface $aggregate, array $parameters);
52
+ }
includes/predis/src/Connection/NodeConnectionInterface.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * Defines a connection used to communicate with a single Redis node.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ interface NodeConnectionInterface extends ConnectionInterface
22
+ {
23
+ /**
24
+ * Returns a string representation of the connection.
25
+ *
26
+ * @return string
27
+ */
28
+ public function __toString();
29
+
30
+ /**
31
+ * Returns the underlying resource used to communicate with Redis.
32
+ *
33
+ * @return mixed
34
+ */
35
+ public function getResource();
36
+
37
+ /**
38
+ * Returns the parameters used to initialize the connection.
39
+ *
40
+ * @return ParametersInterface
41
+ */
42
+ public function getParameters();
43
+
44
+ /**
45
+ * Pushes the given command into a queue of commands executed when
46
+ * establishing the actual connection to Redis.
47
+ *
48
+ * @param CommandInterface $command Instance of a Redis command.
49
+ */
50
+ public function addConnectCommand(CommandInterface $command);
51
+
52
+ /**
53
+ * Reads a response from the server.
54
+ *
55
+ * @return mixed
56
+ */
57
+ public function read();
58
+ }
includes/predis/src/Connection/Parameters.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ /**
15
+ * Container for connection parameters used to initialize connections to Redis.
16
+ *
17
+ * {@inheritdoc}
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ class Parameters implements ParametersInterface
22
+ {
23
+ private $parameters;
24
+
25
+ private static $defaults = array(
26
+ 'scheme' => 'tcp',
27
+ 'host' => '127.0.0.1',
28
+ 'port' => 6379,
29
+ );
30
+
31
+ /**
32
+ * @param array $parameters Named array of connection parameters.
33
+ */
34
+ public function __construct(array $parameters = array())
35
+ {
36
+ $this->parameters = $this->filter($parameters) + $this->getDefaults();
37
+ }
38
+
39
+ /**
40
+ * Returns some default parameters with their values.
41
+ *
42
+ * @return array
43
+ */
44
+ protected function getDefaults()
45
+ {
46
+ return self::$defaults;
47
+ }
48
+
49
+ /**
50
+ * Creates a new instance by supplying the initial parameters either in the
51
+ * form of an URI string or a named array.
52
+ *
53
+ * @param array|string $parameters Set of connection parameters.
54
+ *
55
+ * @return Parameters
56
+ */
57
+ public static function create($parameters)
58
+ {
59
+ if (is_string($parameters)) {
60
+ $parameters = static::parse($parameters);
61
+ }
62
+
63
+ return new static($parameters ?: array());
64
+ }
65
+
66
+ /**
67
+ * Parses an URI string returning an array of connection parameters.
68
+ *
69
+ * When using the "redis" and "rediss" schemes the URI is parsed according
70
+ * to the rules defined by the provisional registration documents approved
71
+ * by IANA. If the URI has a password in its "user-information" part or a
72
+ * database number in the "path" part these values override the values of
73
+ * "password" and "database" if they are present in the "query" part.
74
+ *
75
+ * @link http://www.iana.org/assignments/uri-schemes/prov/redis
76
+ * @link http://www.iana.org/assignments/uri-schemes/prov/rediss
77
+ *
78
+ * @param string $uri URI string.
79
+ *
80
+ * @throws \InvalidArgumentException
81
+ *
82
+ * @return array
83
+ */
84
+ public static function parse($uri)
85
+ {
86
+ if (stripos($uri, 'unix://') === 0) {
87
+ // parse_url() can parse unix:/path/to/sock so we do not need the
88
+ // unix:///path/to/sock hack, we will support it anyway until 2.0.
89
+ $uri = str_ireplace('unix://', 'unix:', $uri);
90
+ }
91
+
92
+ if (!$parsed = parse_url($uri)) {
93
+ throw new \InvalidArgumentException("Invalid parameters URI: $uri");
94
+ }
95
+
96
+ if (
97
+ isset($parsed['host'])
98
+ && false !== strpos($parsed['host'], '[')
99
+ && false !== strpos($parsed['host'], ']')
100
+ ) {
101
+ $parsed['host'] = substr($parsed['host'], 1, -1);
102
+ }
103
+
104
+ if (isset($parsed['query'])) {
105
+ parse_str($parsed['query'], $queryarray);
106
+ unset($parsed['query']);
107
+
108
+ $parsed = array_merge($parsed, $queryarray);
109
+ }
110
+
111
+ if (stripos($uri, 'redis') === 0) {
112
+ if (isset($parsed['pass'])) {
113
+ $parsed['password'] = $parsed['pass'];
114
+ unset($parsed['pass']);
115
+ }
116
+
117
+ if (isset($parsed['path']) && preg_match('/^\/(\d+)(\/.*)?/', $parsed['path'], $path)) {
118
+ $parsed['database'] = $path[1];
119
+
120
+ if (isset($path[2])) {
121
+ $parsed['path'] = $path[2];
122
+ } else {
123
+ unset($parsed['path']);
124
+ }
125
+ }
126
+ }
127
+
128
+ return $parsed;
129
+ }
130
+
131
+ /**
132
+ * Validates and converts each value of the connection parameters array.
133
+ *
134
+ * @param array $parameters Connection parameters.
135
+ *
136
+ * @return array
137
+ */
138
+ protected function filter(array $parameters)
139
+ {
140
+ return $parameters ?: array();
141
+ }
142
+
143
+ /**
144
+ * {@inheritdoc}
145
+ */
146
+ public function __get($parameter)
147
+ {
148
+ if (isset($this->parameters[$parameter])) {
149
+ return $this->parameters[$parameter];
150
+ }
151
+ }
152
+
153
+ /**
154
+ * {@inheritdoc}
155
+ */
156
+ public function __isset($parameter)
157
+ {
158
+ return isset($this->parameters[$parameter]);
159
+ }
160
+
161
+ /**
162
+ * {@inheritdoc}
163
+ */
164
+ public function toArray()
165
+ {
166
+ return $this->parameters;
167
+ }
168
+
169
+ /**
170
+ * {@inheritdoc}
171
+ */
172
+ public function __sleep()
173
+ {
174
+ return array('parameters');
175
+ }
176
+ }
includes/predis/src/Connection/ParametersInterface.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ /**
15
+ * Interface defining a container for connection parameters.
16
+ *
17
+ * The actual list of connection parameters depends on the features supported by
18
+ * each connection backend class (please refer to their specific documentation),
19
+ * but the most common parameters used through the library are:
20
+ *
21
+ * @property-read string scheme Connection scheme, such as 'tcp' or 'unix'.
22
+ * @property-read string host IP address or hostname of Redis.
23
+ * @property-read int port TCP port on which Redis is listening to.
24
+ * @property-read string path Path of a UNIX domain socket file.
25
+ * @property-read string alias Alias for the connection.
26
+ * @property-read float timeout Timeout for the connect() operation.
27
+ * @property-read float read_write_timeout Timeout for read() and write() operations.
28
+ * @property-read bool async_connect Performs the connect() operation asynchronously.
29
+ * @property-read bool tcp_nodelay Toggles the Nagle's algorithm for coalescing.
30
+ * @property-read bool persistent Leaves the connection open after a GC collection.
31
+ * @property-read string password Password to access Redis (see the AUTH command).
32
+ * @property-read string database Database index (see the SELECT command).
33
+ *
34
+ * @author Daniele Alessandri <suppakilla@gmail.com>
35
+ */
36
+ interface ParametersInterface
37
+ {
38
+ /**
39
+ * Checks if the specified parameters is set.
40
+ *
41
+ * @param string $parameter Name of the parameter.
42
+ *
43
+ * @return bool
44
+ */
45
+ public function __isset($parameter);
46
+
47
+ /**
48
+ * Returns the value of the specified parameter.
49
+ *
50
+ * @param string $parameter Name of the parameter.
51
+ *
52
+ * @return mixed|null
53
+ */
54
+ public function __get($parameter);
55
+
56
+ /**
57
+ * Returns an array representation of the connection parameters.
58
+ *
59
+ * @return array
60
+ */
61
+ public function toArray();
62
+ }
includes/predis/src/Connection/PhpiredisSocketConnection.php ADDED
@@ -0,0 +1,418 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\NotSupportedException;
16
+ use Predis\Response\Error as ErrorResponse;
17
+ use Predis\Response\ErrorInterface as ErrorResponseInterface;
18
+ use Predis\Response\Status as StatusResponse;
19
+
20
+ /**
21
+ * This class provides the implementation of a Predis connection that uses the
22
+ * PHP socket extension for network communication and wraps the phpiredis C
23
+ * extension (PHP bindings for hiredis) to parse the Redis protocol.
24
+ *
25
+ * This class is intended to provide an optional low-overhead alternative for
26
+ * processing responses from Redis compared to the standard pure-PHP classes.
27
+ * Differences in speed when dealing with short inline responses are practically
28
+ * nonexistent, the actual speed boost is for big multibulk responses when this
29
+ * protocol processor can parse and return responses very fast.
30
+ *
31
+ * For instructions on how to build and install the phpiredis extension, please
32
+ * consult the repository of the project.
33
+ *
34
+ * The connection parameters supported by this class are:
35
+ *
36
+ * - scheme: it can be either 'redis', 'tcp' or 'unix'.
37
+ * - host: hostname or IP address of the server.
38
+ * - port: TCP port of the server.
39
+ * - path: path of a UNIX domain socket when scheme is 'unix'.
40
+ * - timeout: timeout to perform the connection (default is 5 seconds).
41
+ * - read_write_timeout: timeout of read / write operations.
42
+ *
43
+ * @link http://github.com/nrk/phpiredis
44
+ *
45
+ * @author Daniele Alessandri <suppakilla@gmail.com>
46
+ */
47
+ class PhpiredisSocketConnection extends AbstractConnection
48
+ {
49
+ private $reader;
50
+
51
+ /**
52
+ * {@inheritdoc}
53
+ */
54
+ public function __construct(ParametersInterface $parameters)
55
+ {
56
+ $this->assertExtensions();
57
+
58
+ parent::__construct($parameters);
59
+
60
+ $this->reader = $this->createReader();
61
+ }
62
+
63
+ /**
64
+ * Disconnects from the server and destroys the underlying resource and the
65
+ * protocol reader resource when PHP's garbage collector kicks in.
66
+ */
67
+ public function __destruct()
68
+ {
69
+ phpiredis_reader_destroy($this->reader);
70
+
71
+ parent::__destruct();
72
+ }
73
+
74
+ /**
75
+ * Checks if the socket and phpiredis extensions are loaded in PHP.
76
+ */
77
+ protected function assertExtensions()
78
+ {
79
+ if (!extension_loaded('sockets')) {
80
+ throw new NotSupportedException(
81
+ 'The "sockets" extension is required by this connection backend.'
82
+ );
83
+ }
84
+
85
+ if (!extension_loaded('phpiredis')) {
86
+ throw new NotSupportedException(
87
+ 'The "phpiredis" extension is required by this connection backend.'
88
+ );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * {@inheritdoc}
94
+ */
95
+ protected function assertParameters(ParametersInterface $parameters)
96
+ {
97
+ switch ($parameters->scheme) {
98
+ case 'tcp':
99
+ case 'redis':
100
+ case 'unix':
101
+ break;
102
+
103
+ default:
104
+ throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
105
+ }
106
+
107
+ if (isset($parameters->persistent)) {
108
+ throw new NotSupportedException(
109
+ 'Persistent connections are not supported by this connection backend.'
110
+ );
111
+ }
112
+
113
+ return $parameters;
114
+ }
115
+
116
+ /**
117
+ * Creates a new instance of the protocol reader resource.
118
+ *
119
+ * @return resource
120
+ */
121
+ private function createReader()
122
+ {
123
+ $reader = phpiredis_reader_create();
124
+
125
+ phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
126
+ phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
127
+
128
+ return $reader;
129
+ }
130
+
131
+ /**
132
+ * Returns the underlying protocol reader resource.
133
+ *
134
+ * @return resource
135
+ */
136
+ protected function getReader()
137
+ {
138
+ return $this->reader;
139
+ }
140
+
141
+ /**
142
+ * Returns the handler used by the protocol reader for inline responses.
143
+ *
144
+ * @return \Closure
145
+ */
146
+ protected function getStatusHandler()
147
+ {
148
+ static $statusHandler;
149
+
150
+ if (!$statusHandler) {
151
+ $statusHandler = function ($payload) {
152
+ return StatusResponse::get($payload);
153
+ };
154
+ }
155
+
156
+ return $statusHandler;
157
+ }
158
+
159
+ /**
160
+ * Returns the handler used by the protocol reader for error responses.
161
+ *
162
+ * @return \Closure
163
+ */
164
+ protected function getErrorHandler()
165
+ {
166
+ static $errorHandler;
167
+
168
+ if (!$errorHandler) {
169
+ $errorHandler = function ($errorMessage) {
170
+ return new ErrorResponse($errorMessage);
171
+ };
172
+ }
173
+
174
+ return $errorHandler;
175
+ }
176
+
177
+ /**
178
+ * Helper method used to throw exceptions on socket errors.
179
+ */
180
+ private function emitSocketError()
181
+ {
182
+ $errno = socket_last_error();
183
+ $errstr = socket_strerror($errno);
184
+
185
+ $this->disconnect();
186
+
187
+ $this->onConnectionError(trim($errstr), $errno);
188
+ }
189
+
190
+ /**
191
+ * Gets the address of an host from connection parameters.
192
+ *
193
+ * @param ParametersInterface $parameters Parameters used to initialize the connection.
194
+ *
195
+ * @return string
196
+ */
197
+ protected static function getAddress(ParametersInterface $parameters)
198
+ {
199
+ if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) {
200
+ return $host;
201
+ }
202
+
203
+ if ($host === $address = gethostbyname($host)) {
204
+ return false;
205
+ }
206
+
207
+ return $address;
208
+ }
209
+
210
+ /**
211
+ * {@inheritdoc}
212
+ */
213
+ protected function createResource()
214
+ {
215
+ $parameters = $this->parameters;
216
+
217
+ if ($parameters->scheme === 'unix') {
218
+ $address = $parameters->path;
219
+ $domain = AF_UNIX;
220
+ $protocol = 0;
221
+ } else {
222
+ if (false === $address = self::getAddress($parameters)) {
223
+ $this->onConnectionError("Cannot resolve the address of '$parameters->host'.");
224
+ }
225
+
226
+ $domain = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? AF_INET6 : AF_INET;
227
+ $protocol = SOL_TCP;
228
+ }
229
+
230
+ $socket = @socket_create($domain, SOCK_STREAM, $protocol);
231
+
232
+ if (!is_resource($socket)) {
233
+ $this->emitSocketError();
234
+ }
235
+
236
+ $this->setSocketOptions($socket, $parameters);
237
+ $this->connectWithTimeout($socket, $address, $parameters);
238
+
239
+ return $socket;
240
+ }
241
+
242
+ /**
243
+ * Sets options on the socket resource from the connection parameters.
244
+ *
245
+ * @param resource $socket Socket resource.
246
+ * @param ParametersInterface $parameters Parameters used to initialize the connection.
247
+ */
248
+ private function setSocketOptions($socket, ParametersInterface $parameters)
249
+ {
250
+ if ($parameters->scheme !== 'unix') {
251
+ if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) {
252
+ $this->emitSocketError();
253
+ }
254
+
255
+ if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
256
+ $this->emitSocketError();
257
+ }
258
+ }
259
+
260
+ if (isset($parameters->read_write_timeout)) {
261
+ $rwtimeout = (float) $parameters->read_write_timeout;
262
+ $timeoutSec = floor($rwtimeout);
263
+ $timeoutUsec = ($rwtimeout - $timeoutSec) * 1000000;
264
+
265
+ $timeout = array(
266
+ 'sec' => $timeoutSec,
267
+ 'usec' => $timeoutUsec,
268
+ );
269
+
270
+ if (!socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)) {
271
+ $this->emitSocketError();
272
+ }
273
+
274
+ if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)) {
275
+ $this->emitSocketError();
276
+ }
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Opens the actual connection to the server with a timeout.
282
+ *
283
+ * @param resource $socket Socket resource.
284
+ * @param string $address IP address (DNS-resolved from hostname)
285
+ * @param ParametersInterface $parameters Parameters used to initialize the connection.
286
+ *
287
+ * @return string
288
+ */
289
+ private function connectWithTimeout($socket, $address, ParametersInterface $parameters)
290
+ {
291
+ socket_set_nonblock($socket);
292
+
293
+ if (@socket_connect($socket, $address, (int) $parameters->port) === false) {
294
+ $error = socket_last_error();
295
+
296
+ if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) {
297
+ $this->emitSocketError();
298
+ }
299
+ }
300
+
301
+ socket_set_block($socket);
302
+
303
+ $null = null;
304
+ $selectable = array($socket);
305
+
306
+ $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
307
+ $timeoutSecs = floor($timeout);
308
+ $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000;
309
+
310
+ $selected = socket_select($selectable, $selectable, $null, $timeoutSecs, $timeoutUSecs);
311
+
312
+ if ($selected === 2) {
313
+ $this->onConnectionError('Connection refused.', SOCKET_ECONNREFUSED);
314
+ }
315
+
316
+ if ($selected === 0) {
317
+ $this->onConnectionError('Connection timed out.', SOCKET_ETIMEDOUT);
318
+ }
319
+
320
+ if ($selected === false) {
321
+ $this->emitSocketError();
322
+ }
323
+ }
324
+
325
+ /**
326
+ * {@inheritdoc}
327
+ */
328
+ public function connect()
329
+ {
330
+ if (parent::connect() && $this->initCommands) {
331
+ foreach ($this->initCommands as $command) {
332
+ $response = $this->executeCommand($command);
333
+
334
+ if ($response instanceof ErrorResponseInterface) {
335
+ $this->onConnectionError("`{$command->getId()}` failed: $response", 0);
336
+ }
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * {@inheritdoc}
343
+ */
344
+ public function disconnect()
345
+ {
346
+ if ($this->isConnected()) {
347
+ socket_close($this->getResource());
348
+ parent::disconnect();
349
+ }
350
+ }
351
+
352
+ /**
353
+ * {@inheritdoc}
354
+ */
355
+ protected function write($buffer)
356
+ {
357
+ $socket = $this->getResource();
358
+
359
+ while (($length = strlen($buffer)) > 0) {
360
+ $written = socket_write($socket, $buffer, $length);
361
+
362
+ if ($length === $written) {
363
+ return;
364
+ }
365
+
366
+ if ($written === false) {
367
+ $this->onConnectionError('Error while writing bytes to the server.');
368
+ }
369
+
370
+ $buffer = substr($buffer, $written);
371
+ }
372
+ }
373
+
374
+ /**
375
+ * {@inheritdoc}
376
+ */
377
+ public function read()
378
+ {
379
+ $socket = $this->getResource();
380
+ $reader = $this->reader;
381
+
382
+ while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
383
+ if (@socket_recv($socket, $buffer, 4096, 0) === false || $buffer === '' || $buffer === null) {
384
+ $this->emitSocketError();
385
+ }
386
+
387
+ phpiredis_reader_feed($reader, $buffer);
388
+ }
389
+
390
+ if ($state === PHPIREDIS_READER_STATE_COMPLETE) {
391
+ return phpiredis_reader_get_reply($reader);
392
+ } else {
393
+ $this->onProtocolError(phpiredis_reader_get_error($reader));
394
+
395
+ return;
396
+ }
397
+ }
398
+
399
+ /**
400
+ * {@inheritdoc}
401
+ */
402
+ public function writeRequest(CommandInterface $command)
403
+ {
404
+ $arguments = $command->getArguments();
405
+ array_unshift($arguments, $command->getId());
406
+
407
+ $this->write(phpiredis_format_command($arguments));
408
+ }
409
+
410
+ /**
411
+ * {@inheritdoc}
412
+ */
413
+ public function __wakeup()
414
+ {
415
+ $this->assertExtensions();
416
+ $this->reader = $this->createReader();
417
+ }
418
+ }
includes/predis/src/Connection/PhpiredisStreamConnection.php ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\NotSupportedException;
16
+ use Predis\Response\Error as ErrorResponse;
17
+ use Predis\Response\Status as StatusResponse;
18
+
19
+ /**
20
+ * This class provides the implementation of a Predis connection that uses PHP's
21
+ * streams for network communication and wraps the phpiredis C extension (PHP
22
+ * bindings for hiredis) to parse and serialize the Redis protocol.
23
+ *
24
+ * This class is intended to provide an optional low-overhead alternative for
25
+ * processing responses from Redis compared to the standard pure-PHP classes.
26
+ * Differences in speed when dealing with short inline responses are practically
27
+ * nonexistent, the actual speed boost is for big multibulk responses when this
28
+ * protocol processor can parse and return responses very fast.
29
+ *
30
+ * For instructions on how to build and install the phpiredis extension, please
31
+ * consult the repository of the project.
32
+ *
33
+ * The connection parameters supported by this class are:
34
+ *
35
+ * - scheme: it can be either 'redis', 'tcp' or 'unix'.
36
+ * - host: hostname or IP address of the server.
37
+ * - port: TCP port of the server.
38
+ * - path: path of a UNIX domain socket when scheme is 'unix'.
39
+ * - timeout: timeout to perform the connection.
40
+ * - read_write_timeout: timeout of read / write operations.
41
+ * - async_connect: performs the connection asynchronously.
42
+ * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing.
43
+ * - persistent: the connection is left intact after a GC collection.
44
+ *
45
+ * @link https://github.com/nrk/phpiredis
46
+ *
47
+ * @author Daniele Alessandri <suppakilla@gmail.com>
48
+ */
49
+ class PhpiredisStreamConnection extends StreamConnection
50
+ {
51
+ private $reader;
52
+
53
+ /**
54
+ * {@inheritdoc}
55
+ */
56
+ public function __construct(ParametersInterface $parameters)
57
+ {
58
+ $this->assertExtensions();
59
+
60
+ parent::__construct($parameters);
61
+
62
+ $this->reader = $this->createReader();
63
+ }
64
+
65
+ /**
66
+ * {@inheritdoc}
67
+ */
68
+ public function __destruct()
69
+ {
70
+ phpiredis_reader_destroy($this->reader);
71
+
72
+ parent::__destruct();
73
+ }
74
+
75
+ /**
76
+ * Checks if the phpiredis extension is loaded in PHP.
77
+ */
78
+ private function assertExtensions()
79
+ {
80
+ if (!extension_loaded('phpiredis')) {
81
+ throw new NotSupportedException(
82
+ 'The "phpiredis" extension is required by this connection backend.'
83
+ );
84
+ }
85
+ }
86
+
87
+ /**
88
+ * {@inheritdoc}
89
+ */
90
+ protected function assertSslSupport(ParametersInterface $parameters)
91
+ {
92
+ throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.');
93
+ }
94
+
95
+ /**
96
+ * {@inheritdoc}
97
+ */
98
+ protected function createStreamSocket(ParametersInterface $parameters, $address, $flags, $context = null)
99
+ {
100
+ $socket = null;
101
+ $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
102
+
103
+ $resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags);
104
+
105
+ if (!$resource) {
106
+ $this->onConnectionError(trim($errstr), $errno);
107
+ }
108
+
109
+ if (isset($parameters->read_write_timeout) && function_exists('socket_import_stream')) {
110
+ $rwtimeout = (float) $parameters->read_write_timeout;
111
+ $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1;
112
+
113
+ $timeout = array(
114
+ 'sec' => $timeoutSeconds = floor($rwtimeout),
115
+ 'usec' => ($rwtimeout - $timeoutSeconds) * 1000000,
116
+ );
117
+
118
+ $socket = $socket ?: socket_import_stream($resource);
119
+ @socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout);
120
+ @socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
121
+ }
122
+
123
+ if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) {
124
+ $socket = $socket ?: socket_import_stream($resource);
125
+ socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
126
+ }
127
+
128
+ return $resource;
129
+ }
130
+
131
+ /**
132
+ * Creates a new instance of the protocol reader resource.
133
+ *
134
+ * @return resource
135
+ */
136
+ private function createReader()
137
+ {
138
+ $reader = phpiredis_reader_create();
139
+
140
+ phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
141
+ phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
142
+
143
+ return $reader;
144
+ }
145
+
146
+ /**
147
+ * Returns the underlying protocol reader resource.
148
+ *
149
+ * @return resource
150
+ */
151
+ protected function getReader()
152
+ {
153
+ return $this->reader;
154
+ }
155
+
156
+ /**
157
+ * Returns the handler used by the protocol reader for inline responses.
158
+ *
159
+ * @return \Closure
160
+ */
161
+ protected function getStatusHandler()
162
+ {
163
+ static $statusHandler;
164
+
165
+ if (!$statusHandler) {
166
+ $statusHandler = function ($payload) {
167
+ return StatusResponse::get($payload);
168
+ };
169
+ }
170
+
171
+ return $statusHandler;
172
+ }
173
+
174
+ /**
175
+ * Returns the handler used by the protocol reader for error responses.
176
+ *
177
+ * @return \Closure
178
+ */
179
+ protected function getErrorHandler()
180
+ {
181
+ static $errorHandler;
182
+
183
+ if (!$errorHandler) {
184
+ $errorHandler = function ($errorMessage) {
185
+ return new ErrorResponse($errorMessage);
186
+ };
187
+ }
188
+
189
+ return $errorHandler;
190
+ }
191
+
192
+ /**
193
+ * {@inheritdoc}
194
+ */
195
+ public function read()
196
+ {
197
+ $socket = $this->getResource();
198
+ $reader = $this->reader;
199
+
200
+ while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
201
+ $buffer = stream_socket_recvfrom($socket, 4096);
202
+
203
+ if ($buffer === false || $buffer === '') {
204
+ $this->onConnectionError('Error while reading bytes from the server.');
205
+ }
206
+
207
+ phpiredis_reader_feed($reader, $buffer);
208
+ }
209
+
210
+ if ($state === PHPIREDIS_READER_STATE_COMPLETE) {
211
+ return phpiredis_reader_get_reply($reader);
212
+ } else {
213
+ $this->onProtocolError(phpiredis_reader_get_error($reader));
214
+
215
+ return;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * {@inheritdoc}
221
+ */
222
+ public function writeRequest(CommandInterface $command)
223
+ {
224
+ $arguments = $command->getArguments();
225
+ array_unshift($arguments, $command->getId());
226
+
227
+ $this->write(phpiredis_format_command($arguments));
228
+ }
229
+
230
+ /**
231
+ * {@inheritdoc}
232
+ */
233
+ public function __wakeup()
234
+ {
235
+ $this->assertExtensions();
236
+ $this->reader = $this->createReader();
237
+ }
238
+ }
includes/predis/src/Connection/StreamConnection.php ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Response\Error as ErrorResponse;
16
+ use Predis\Response\ErrorInterface as ErrorResponseInterface;
17
+ use Predis\Response\Status as StatusResponse;
18
+
19
+ /**
20
+ * Standard connection to Redis servers implemented on top of PHP's streams.
21
+ * The connection parameters supported by this class are:.
22
+ *
23
+ * - scheme: it can be either 'redis', 'tcp', 'rediss', 'tls' or 'unix'.
24
+ * - host: hostname or IP address of the server.
25
+ * - port: TCP port of the server.
26
+ * - path: path of a UNIX domain socket when scheme is 'unix'.
27
+ * - timeout: timeout to perform the connection (default is 5 seconds).
28
+ * - read_write_timeout: timeout of read / write operations.
29
+ * - async_connect: performs the connection asynchronously.
30
+ * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing.
31
+ * - persistent: the connection is left intact after a GC collection.
32
+ * - ssl: context options array (see http://php.net/manual/en/context.ssl.php)
33
+ *
34
+ * @author Daniele Alessandri <suppakilla@gmail.com>
35
+ */
36
+ class StreamConnection extends AbstractConnection
37
+ {
38
+ /**
39
+ * Disconnects from the server and destroys the underlying resource when the
40
+ * garbage collector kicks in only if the connection has not been marked as
41
+ * persistent.
42
+ */
43
+ public function __destruct()
44
+ {
45
+ if (isset($this->parameters->persistent) && $this->parameters->persistent) {
46
+ return;
47
+ }
48
+
49
+ $this->disconnect();
50
+ }
51
+
52
+ /**
53
+ * {@inheritdoc}
54
+ */
55
+ protected function assertParameters(ParametersInterface $parameters)
56
+ {
57
+ switch ($parameters->scheme) {
58
+ case 'tcp':
59
+ case 'redis':
60
+ case 'unix':
61
+ break;
62
+
63
+ case 'tls':
64
+ case 'rediss':
65
+ $this->assertSslSupport($parameters);
66
+ break;
67
+
68
+ default:
69
+ throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
70
+ }
71
+
72
+ return $parameters;
73
+ }
74
+
75
+ /**
76
+ * Checks needed conditions for SSL-encrypted connections.
77
+ *
78
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
79
+ *
80
+ * @throws \InvalidArgumentException
81
+ */
82
+ protected function assertSslSupport(ParametersInterface $parameters)
83
+ {
84
+ if (
85
+ filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN) &&
86
+ version_compare(PHP_VERSION, '7.0.0beta') < 0
87
+ ) {
88
+ throw new \InvalidArgumentException('Persistent SSL connections require PHP >= 7.0.0.');
89
+ }
90
+ }
91
+
92
+ /**
93
+ * {@inheritdoc}
94
+ */
95
+ protected function createResource()
96
+ {
97
+ switch ($this->parameters->scheme) {
98
+ case 'tcp':
99
+ case 'redis':
100
+ return $this->tcpStreamInitializer($this->parameters);
101
+
102
+ case 'unix':
103
+ return $this->unixStreamInitializer($this->parameters);
104
+
105
+ case 'tls':
106
+ case 'rediss':
107
+ return $this->tlsStreamInitializer($this->parameters);
108
+
109
+ default:
110
+ throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'.");
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Creates a connected stream socket resource.
116
+ *
117
+ * @param ParametersInterface $parameters Connection parameters.
118
+ * @param string $address Address for stream_socket_client().
119
+ * @param int $flags Flags for stream_socket_client().
120
+ *
121
+ * @return resource
122
+ */
123
+ protected function createStreamSocket(ParametersInterface $parameters, $address, $flags)
124
+ {
125
+ $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
126
+
127
+ if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) {
128
+ $this->onConnectionError(trim($errstr), $errno);
129
+ }
130
+
131
+ if (isset($parameters->read_write_timeout)) {
132
+ $rwtimeout = (float) $parameters->read_write_timeout;
133
+ $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1;
134
+ $timeoutSeconds = floor($rwtimeout);
135
+ $timeoutUSeconds = ($rwtimeout - $timeoutSeconds) * 1000000;
136
+ stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds);
137
+ }
138
+
139
+ if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) {
140
+ $socket = socket_import_stream($resource);
141
+ socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
142
+ }
143
+
144
+ return $resource;
145
+ }
146
+
147
+ /**
148
+ * Initializes a TCP stream resource.
149
+ *
150
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
151
+ *
152
+ * @return resource
153
+ */
154
+ protected function tcpStreamInitializer(ParametersInterface $parameters)
155
+ {
156
+ if (!filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
157
+ $address = "tcp://$parameters->host:$parameters->port";
158
+ } else {
159
+ $address = "tcp://[$parameters->host]:$parameters->port";
160
+ }
161
+
162
+ $flags = STREAM_CLIENT_CONNECT;
163
+
164
+ if (isset($parameters->async_connect) && $parameters->async_connect) {
165
+ $flags |= STREAM_CLIENT_ASYNC_CONNECT;
166
+ }
167
+
168
+ if (isset($parameters->persistent)) {
169
+ if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
170
+ $flags |= STREAM_CLIENT_PERSISTENT;
171
+
172
+ if ($persistent === null) {
173
+ $address = "{$address}/{$parameters->persistent}";
174
+ }
175
+ }
176
+ }
177
+
178
+ $resource = $this->createStreamSocket($parameters, $address, $flags);
179
+
180
+ return $resource;
181
+ }
182
+
183
+ /**
184
+ * Initializes a UNIX stream resource.
185
+ *
186
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
187
+ *
188
+ * @return resource
189
+ */
190
+ protected function unixStreamInitializer(ParametersInterface $parameters)
191
+ {
192
+ if (!isset($parameters->path)) {
193
+ throw new \InvalidArgumentException('Missing UNIX domain socket path.');
194
+ }
195
+
196
+ $flags = STREAM_CLIENT_CONNECT;
197
+
198
+ if (isset($parameters->persistent)) {
199
+ if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
200
+ $flags |= STREAM_CLIENT_PERSISTENT;
201
+
202
+ if ($persistent === null) {
203
+ throw new \InvalidArgumentException(
204
+ 'Persistent connection IDs are not supported when using UNIX domain sockets.'
205
+ );
206
+ }
207
+ }
208
+ }
209
+
210
+ $resource = $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags);
211
+
212
+ return $resource;
213
+ }
214
+
215
+ /**
216
+ * Initializes a SSL-encrypted TCP stream resource.
217
+ *
218
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
219
+ *
220
+ * @return resource
221
+ */
222
+ protected function tlsStreamInitializer(ParametersInterface $parameters)
223
+ {
224
+ $resource = $this->tcpStreamInitializer($parameters);
225
+ $metadata = stream_get_meta_data($resource);
226
+
227
+ // Detect if crypto mode is already enabled for this stream (PHP >= 7.0.0).
228
+ if (isset($metadata['crypto'])) {
229
+ return $resource;
230
+ }
231
+
232
+ if (is_array($parameters->ssl)) {
233
+ $options = $parameters->ssl;
234
+ } else {
235
+ $options = array();
236
+ }
237
+
238
+ if (!isset($options['crypto_type'])) {
239
+ $options['crypto_type'] = STREAM_CRYPTO_METHOD_TLS_CLIENT;
240
+ }
241
+
242
+ if (!stream_context_set_option($resource, array('ssl' => $options))) {
243
+ $this->onConnectionError('Error while setting SSL context options');
244
+ }
245
+
246
+ if (!stream_socket_enable_crypto($resource, true, $options['crypto_type'])) {
247
+ $this->onConnectionError('Error while switching to encrypted communication');
248
+ }
249
+
250
+ return $resource;
251
+ }
252
+
253
+ /**
254
+ * {@inheritdoc}
255
+ */
256
+ public function connect()
257
+ {
258
+ if (parent::connect() && $this->initCommands) {
259
+ foreach ($this->initCommands as $command) {
260
+ $response = $this->executeCommand($command);
261
+
262
+ if ($response instanceof ErrorResponseInterface) {
263
+ $this->onConnectionError("`{$command->getId()}` failed: $response", 0);
264
+ }
265
+ }
266
+ }
267
+ }
268
+
269
+ /**
270
+ * {@inheritdoc}
271
+ */
272
+ public function disconnect()
273
+ {
274
+ if ($this->isConnected()) {
275
+ fclose($this->getResource());
276
+ parent::disconnect();
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Performs a write operation over the stream of the buffer containing a
282
+ * command serialized with the Redis wire protocol.
283
+ *
284
+ * @param string $buffer Representation of a command in the Redis wire protocol.
285
+ */
286
+ protected function write($buffer)
287
+ {
288
+ $socket = $this->getResource();
289
+
290
+ while (($length = strlen($buffer)) > 0) {
291
+ $written = @fwrite($socket, $buffer);
292
+
293
+ if ($length === $written) {
294
+ return;
295
+ }
296
+
297
+ if ($written === false || $written === 0) {
298
+ $this->onConnectionError('Error while writing bytes to the server.');
299
+ }
300
+
301
+ $buffer = substr($buffer, $written);
302
+ }
303
+ }
304
+
305
+ /**
306
+ * {@inheritdoc}
307
+ */
308
+ public function read()
309
+ {
310
+ $socket = $this->getResource();
311
+ $chunk = fgets($socket);
312
+
313
+ if ($chunk === false || $chunk === '') {
314
+ $this->onConnectionError('Error while reading line from the server.');
315
+ }
316
+
317
+ $prefix = $chunk[0];
318
+ $payload = substr($chunk, 1, -2);
319
+
320
+ switch ($prefix) {
321
+ case '+':
322
+ return StatusResponse::get($payload);
323
+
324
+ case '$':
325
+ $size = (int) $payload;
326
+
327
+ if ($size === -1) {
328
+ return;
329
+ }
330
+
331
+ $bulkData = '';
332
+ $bytesLeft = ($size += 2);
333
+
334
+ do {
335
+ $chunk = fread($socket, min($bytesLeft, 4096));
336
+
337
+ if ($chunk === false || $chunk === '') {
338
+ $this->onConnectionError('Error while reading bytes from the server.');
339
+ }
340
+
341
+ $bulkData .= $chunk;
342
+ $bytesLeft = $size - strlen($bulkData);
343
+ } while ($bytesLeft > 0);
344
+
345
+ return substr($bulkData, 0, -2);
346
+
347
+ case '*':
348
+ $count = (int) $payload;
349
+
350
+ if ($count === -1) {
351
+ return;
352
+ }
353
+
354
+ $multibulk = array();
355
+
356
+ for ($i = 0; $i < $count; ++$i) {
357
+ $multibulk[$i] = $this->read();
358
+ }
359
+
360
+ return $multibulk;
361
+
362
+ case ':':
363
+ $integer = (int) $payload;
364
+ return $integer == $payload ? $integer : $payload;
365
+
366
+ case '-':
367
+ return new ErrorResponse($payload);
368
+
369
+ default:
370
+ $this->onProtocolError("Unknown response prefix: '$prefix'.");
371
+
372
+ return;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * {@inheritdoc}
378
+ */
379
+ public function writeRequest(CommandInterface $command)
380
+ {
381
+ $commandID = $command->getId();
382
+ $arguments = $command->getArguments();
383
+
384
+ $cmdlen = strlen($commandID);
385
+ $reqlen = count($arguments) + 1;
386
+
387
+ $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
388
+
389
+ foreach ($arguments as $argument) {
390
+ $arglen = strlen($argument);
391
+ $buffer .= "\${$arglen}\r\n{$argument}\r\n";
392
+ }
393
+
394
+ $this->write($buffer);
395
+ }
396
+ }
includes/predis/src/Connection/WebdisConnection.php ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Connection;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\NotSupportedException;
16
+ use Predis\Protocol\ProtocolException;
17
+ use Predis\Response\Error as ErrorResponse;
18
+ use Predis\Response\Status as StatusResponse;
19
+
20
+ /**
21
+ * This class implements a Predis connection that actually talks with Webdis
22
+ * instead of connecting directly to Redis. It relies on the cURL extension to
23
+ * communicate with the web server and the phpiredis extension to parse the
24
+ * protocol for responses returned in the http response bodies.
25
+ *
26
+ * Some features are not yet available or they simply cannot be implemented:
27
+ * - Pipelining commands.
28
+ * - Publish / Subscribe.
29
+ * - MULTI / EXEC transactions (not yet supported by Webdis).
30
+ *
31
+ * The connection parameters supported by this class are:
32
+ *
33
+ * - scheme: must be 'http'.
34
+ * - host: hostname or IP address of the server.
35
+ * - port: TCP port of the server.
36
+ * - timeout: timeout to perform the connection (default is 5 seconds).
37
+ * - user: username for authentication.
38
+ * - pass: password for authentication.
39
+ *
40
+ * @link http://webd.is
41
+ * @link http://github.com/nicolasff/webdis
42
+ * @link http://github.com/seppo0010/phpiredis
43
+ *
44
+ * @author Daniele Alessandri <suppakilla@gmail.com>
45
+ */
46
+ class WebdisConnection implements NodeConnectionInterface
47
+ {
48
+ private $parameters;
49
+ private $resource;
50
+ private $reader;
51
+
52
+ /**
53
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
54
+ *
55
+ * @throws \InvalidArgumentException
56
+ */
57
+ public function __construct(ParametersInterface $parameters)
58
+ {
59
+ $this->assertExtensions();
60
+
61
+ if ($parameters->scheme !== 'http') {
62
+ throw new \InvalidArgumentException("Invalid scheme: '{$parameters->scheme}'.");
63
+ }
64
+
65
+ $this->parameters = $parameters;
66
+
67
+ $this->resource = $this->createCurl();
68
+ $this->reader = $this->createReader();
69
+ }
70
+
71
+ /**
72
+ * Frees the underlying cURL and protocol reader resources when the garbage
73
+ * collector kicks in.
74
+ */
75
+ public function __destruct()
76
+ {
77
+ curl_close($this->resource);
78
+ phpiredis_reader_destroy($this->reader);
79
+ }
80
+
81
+ /**
82
+ * Helper method used to throw on unsupported methods.
83
+ *
84
+ * @param string $method Name of the unsupported method.
85
+ *
86
+ * @throws NotSupportedException
87
+ */
88
+ private function throwNotSupportedException($method)
89
+ {
90
+ $class = __CLASS__;
91
+ throw new NotSupportedException("The method $class::$method() is not supported.");
92
+ }
93
+
94
+ /**
95
+ * Checks if the cURL and phpiredis extensions are loaded in PHP.
96
+ */
97
+ private function assertExtensions()
98
+ {
99
+ if (!extension_loaded('curl')) {
100
+ throw new NotSupportedException(
101
+ 'The "curl" extension is required by this connection backend.'
102
+ );
103
+ }
104
+
105
+ if (!extension_loaded('phpiredis')) {
106
+ throw new NotSupportedException(
107
+ 'The "phpiredis" extension is required by this connection backend.'
108
+ );
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Initializes cURL.
114
+ *
115
+ * @return resource
116
+ */
117
+ private function createCurl()
118
+ {
119
+ $parameters = $this->getParameters();
120
+ $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000;
121
+
122
+ if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) {
123
+ $host = "[$host]";
124
+ }
125
+
126
+ $options = array(
127
+ CURLOPT_FAILONERROR => true,
128
+ CURLOPT_CONNECTTIMEOUT_MS => $timeout,
129
+ CURLOPT_URL => "$parameters->scheme://$host:$parameters->port",
130
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
131
+ CURLOPT_POST => true,
132
+ CURLOPT_WRITEFUNCTION => array($this, 'feedReader'),
133
+ );
134
+
135
+ if (isset($parameters->user, $parameters->pass)) {
136
+ $options[CURLOPT_USERPWD] = "{$parameters->user}:{$parameters->pass}";
137
+ }
138
+
139
+ curl_setopt_array($resource = curl_init(), $options);
140
+
141
+ return $resource;
142
+ }
143
+
144
+ /**
145
+ * Initializes the phpiredis protocol reader.
146
+ *
147
+ * @return resource
148
+ */
149
+ private function createReader()
150
+ {
151
+ $reader = phpiredis_reader_create();
152
+
153
+ phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
154
+ phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
155
+
156
+ return $reader;
157
+ }
158
+
159
+ /**
160
+ * Returns the handler used by the protocol reader for inline responses.
161
+ *
162
+ * @return \Closure
163
+ */
164
+ protected function getStatusHandler()
165
+ {
166
+ static $statusHandler;
167
+
168
+ if (!$statusHandler) {
169
+ $statusHandler = function ($payload) {
170
+ return StatusResponse::get($payload);
171
+ };
172
+ }
173
+
174
+ return $statusHandler;
175
+ }
176
+
177
+ /**
178
+ * Returns the handler used by the protocol reader for error responses.
179
+ *
180
+ * @return \Closure
181
+ */
182
+ protected function getErrorHandler()
183
+ {
184
+ static $errorHandler;
185
+
186
+ if (!$errorHandler) {
187
+ $errorHandler = function ($errorMessage) {
188
+ return new ErrorResponse($errorMessage);
189
+ };
190
+ }
191
+
192
+ return $errorHandler;
193
+ }
194
+
195
+ /**
196
+ * Feeds the phpredis reader resource with the data read from the network.
197
+ *
198
+ * @param resource $resource Reader resource.
199
+ * @param string $buffer Buffer of data read from a connection.
200
+ *
201
+ * @return int
202
+ */
203
+ protected function feedReader($resource, $buffer)
204
+ {
205
+ phpiredis_reader_feed($this->reader, $buffer);
206
+
207
+ return strlen($buffer);
208
+ }
209
+
210
+ /**
211
+ * {@inheritdoc}
212
+ */
213
+ public function connect()
214
+ {
215
+ // NOOP
216
+ }
217
+
218
+ /**
219
+ * {@inheritdoc}
220
+ */
221
+ public function disconnect()
222
+ {
223
+ // NOOP
224
+ }
225
+
226
+ /**
227
+ * {@inheritdoc}
228
+ */
229
+ public function isConnected()
230
+ {
231
+ return true;
232
+ }
233
+
234
+ /**
235
+ * Checks if the specified command is supported by this connection class.
236
+ *
237
+ * @param CommandInterface $command Command instance.
238
+ *
239
+ * @throws NotSupportedException
240
+ *
241
+ * @return string
242
+ */
243
+ protected function getCommandId(CommandInterface $command)
244
+ {
245
+ switch ($commandID = $command->getId()) {
246
+ case 'AUTH':
247
+ case 'SELECT':
248
+ case 'MULTI':
249
+ case 'EXEC':
250
+ case 'WATCH':
251
+ case 'UNWATCH':
252
+ case 'DISCARD':
253
+ case 'MONITOR':
254
+ throw new NotSupportedException("Command '$commandID' is not allowed by Webdis.");
255
+
256
+ default:
257
+ return $commandID;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * {@inheritdoc}
263
+ */
264
+ public function writeRequest(CommandInterface $command)
265
+ {
266
+ $this->throwNotSupportedException(__FUNCTION__);
267
+ }
268
+
269
+ /**
270
+ * {@inheritdoc}
271
+ */
272
+ public function readResponse(CommandInterface $command)
273
+ {
274
+ $this->throwNotSupportedException(__FUNCTION__);
275
+ }
276
+
277
+ /**
278
+ * {@inheritdoc}
279
+ */
280
+ public function executeCommand(CommandInterface $command)
281
+ {
282
+ $resource = $this->resource;
283
+ $commandId = $this->getCommandId($command);
284
+
285
+ if ($arguments = $command->getArguments()) {
286
+ $arguments = implode('/', array_map('urlencode', $arguments));
287
+ $serializedCommand = "$commandId/$arguments.raw";
288
+ } else {
289
+ $serializedCommand = "$commandId.raw";
290
+ }
291
+
292
+ curl_setopt($resource, CURLOPT_POSTFIELDS, $serializedCommand);
293
+
294
+ if (curl_exec($resource) === false) {
295
+ $error = curl_error($resource);
296
+ $errno = curl_errno($resource);
297
+
298
+ throw new ConnectionException($this, trim($error), $errno);
299
+ }
300
+
301
+ if (phpiredis_reader_get_state($this->reader) !== PHPIREDIS_READER_STATE_COMPLETE) {
302
+ throw new ProtocolException($this, phpiredis_reader_get_error($this->reader));
303
+ }
304
+
305
+ return phpiredis_reader_get_reply($this->reader);
306
+ }
307
+
308
+ /**
309
+ * {@inheritdoc}
310
+ */
311
+ public function getResource()
312
+ {
313
+ return $this->resource;
314
+ }
315
+
316
+ /**
317
+ * {@inheritdoc}
318
+ */
319
+ public function getParameters()
320
+ {
321
+ return $this->parameters;
322
+ }
323
+
324
+ /**
325
+ * {@inheritdoc}
326
+ */
327
+ public function addConnectCommand(CommandInterface $command)
328
+ {
329
+ $this->throwNotSupportedException(__FUNCTION__);
330
+ }
331
+
332
+ /**
333
+ * {@inheritdoc}
334
+ */
335
+ public function read()
336
+ {
337
+ $this->throwNotSupportedException(__FUNCTION__);
338
+ }
339
+
340
+ /**
341
+ * {@inheritdoc}
342
+ */
343
+ public function __toString()
344
+ {
345
+ return "{$this->parameters->host}:{$this->parameters->port}";
346
+ }
347
+
348
+ /**
349
+ * {@inheritdoc}
350
+ */
351
+ public function __sleep()
352
+ {
353
+ return array('parameters');
354
+ }
355
+
356
+ /**
357
+ * {@inheritdoc}
358
+ */
359
+ public function __wakeup()
360
+ {
361
+ $this->assertExtensions();
362
+
363
+ $this->resource = $this->createCurl();
364
+ $this->reader = $this->createReader();
365
+ }
366
+ }
includes/predis/src/Monitor/Consumer.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Monitor;
13
+
14
+ use Predis\ClientInterface;
15
+ use Predis\Connection\AggregateConnectionInterface;
16
+ use Predis\NotSupportedException;
17
+
18
+ /**
19
+ * Redis MONITOR consumer.
20
+ *
21
+ * @author Daniele Alessandri <suppakilla@gmail.com>
22
+ */
23
+ class Consumer implements \Iterator
24
+ {
25
+ private $client;
26
+ private $valid;
27
+ private $position;
28
+
29
+ /**
30
+ * @param ClientInterface $client Client instance used by the consumer.
31
+ */
32
+ public function __construct(ClientInterface $client)
33
+ {
34
+ $this->assertClient($client);
35
+
36
+ $this->client = $client;
37
+
38
+ $this->start();
39
+ }
40
+
41
+ /**
42
+ * Automatically stops the consumer when the garbage collector kicks in.
43
+ */
44
+ public function __destruct()
45
+ {
46
+ $this->stop();
47
+ }
48
+
49
+ /**
50
+ * Checks if the passed client instance satisfies the required conditions
51
+ * needed to initialize a monitor consumer.
52
+ *
53
+ * @param ClientInterface $client Client instance used by the consumer.
54
+ *
55
+ * @throws NotSupportedException
56
+ */
57
+ private function assertClient(ClientInterface $client)
58
+ {
59
+ if ($client->getConnection() instanceof AggregateConnectionInterface) {
60
+ throw new NotSupportedException(
61
+ 'Cannot initialize a monitor consumer over aggregate connections.'
62
+ );
63
+ }
64
+
65
+ if ($client->getProfile()->supportsCommand('MONITOR') === false) {
66
+ throw new NotSupportedException("The current profile does not support 'MONITOR'.");
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Initializes the consumer and sends the MONITOR command to the server.
72
+ */
73
+ protected function start()
74
+ {
75
+ $this->client->executeCommand(
76
+ $this->client->createCommand('MONITOR')
77
+ );
78
+ $this->valid = true;
79
+ }
80
+
81
+ /**
82
+ * Stops the consumer. Internally this is done by disconnecting from server
83
+ * since there is no way to terminate the stream initialized by MONITOR.
84
+ */
85
+ public function stop()
86
+ {
87
+ $this->client->disconnect();
88
+ $this->valid = false;
89
+ }
90
+
91
+ /**
92
+ * {@inheritdoc}
93
+ */
94
+ public function rewind()
95
+ {
96
+ // NOOP
97
+ }
98
+
99
+ /**
100
+ * Returns the last message payload retrieved from the server.
101
+ *
102
+ * @return object
103
+ */
104
+ public function current()
105
+ {
106
+ return $this->getValue();
107
+ }
108
+
109
+ /**
110
+ * {@inheritdoc}
111
+ */
112
+ public function key()
113
+ {
114
+ return $this->position;
115
+ }
116
+
117
+ /**
118
+ * {@inheritdoc}
119
+ */
120
+ public function next()
121
+ {
122
+ ++$this->position;
123
+ }
124
+
125
+ /**
126
+ * Checks if the the consumer is still in a valid state to continue.
127
+ *
128
+ * @return bool
129
+ */
130
+ public function valid()
131
+ {
132
+ return $this->valid;
133
+ }
134
+
135
+ /**
136
+ * Waits for a new message from the server generated by MONITOR and returns
137
+ * it when available.
138
+ *
139
+ * @return object
140
+ */
141
+ private function getValue()
142
+ {
143
+ $database = 0;
144
+ $client = null;
145
+ $event = $this->client->getConnection()->read();
146
+
147
+ $callback = function ($matches) use (&$database, &$client) {
148
+ if (2 === $count = count($matches)) {
149
+ // Redis <= 2.4
150
+ $database = (int) $matches[1];
151
+ }
152
+
153
+ if (4 === $count) {
154
+ // Redis >= 2.6
155
+ $database = (int) $matches[2];
156
+ $client = $matches[3];
157
+ }
158
+
159
+ return ' ';
160
+ };
161
+
162
+ $event = preg_replace_callback('/ \(db (\d+)\) | \[(\d+) (.*?)\] /', $callback, $event, 1);
163
+ @list($timestamp, $command, $arguments) = explode(' ', $event, 3);
164
+
165
+ return (object) array(
166
+ 'timestamp' => (float) $timestamp,
167
+ 'database' => $database,
168
+ 'client' => $client,
169
+ 'command' => substr($command, 1, -1),
170
+ 'arguments' => $arguments,
171
+ );
172
+ }
173
+ }
includes/predis/src/NotSupportedException.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ /**
15
+ * Exception class thrown when trying to use features not supported by certain
16
+ * classes or abstractions of Predis.
17
+ *
18
+ * @author Daniele Alessandri <suppakilla@gmail.com>
19
+ */
20
+ class NotSupportedException extends PredisException
21
+ {
22
+ }
includes/predis/src/Pipeline/Atomic.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Pipeline;
13
+
14
+ use Predis\ClientException;
15
+ use Predis\ClientInterface;
16
+ use Predis\Connection\ConnectionInterface;
17
+ use Predis\Connection\NodeConnectionInterface;
18
+ use Predis\Response\ErrorInterface as ErrorResponseInterface;
19
+ use Predis\Response\ResponseInterface;
20
+ use Predis\Response\ServerException;
21
+
22
+ /**
23
+ * Command pipeline wrapped into a MULTI / EXEC transaction.
24
+ *
25
+ * @author Daniele Alessandri <suppakilla@gmail.com>
26
+ */
27
+ class Atomic extends Pipeline
28
+ {
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function __construct(ClientInterface $client)
33
+ {
34
+ if (!$client->getProfile()->supportsCommands(array('multi', 'exec', 'discard'))) {
35
+ throw new ClientException(
36
+ "The current profile does not support 'MULTI', 'EXEC' and 'DISCARD'."
37
+ );
38
+ }
39
+
40
+ parent::__construct($client);
41
+ }
42
+
43
+ /**
44
+ * {@inheritdoc}
45
+ */
46
+ protected function getConnection()
47
+ {
48
+ $connection = $this->getClient()->getConnection();
49
+
50
+ if (!$connection instanceof NodeConnectionInterface) {
51
+ $class = __CLASS__;
52
+
53
+ throw new ClientException("The class '$class' does not support aggregate connections.");
54
+ }
55
+
56
+ return $connection;
57
+ }
58
+
59
+ /**
60
+ * {@inheritdoc}
61
+ */
62
+ protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
63
+ {
64
+ $profile = $this->getClient()->getProfile();
65
+ $connection->executeCommand($profile->createCommand('multi'));
66
+
67
+ foreach ($commands as $command) {
68
+ $connection->writeRequest($command);
69
+ }
70
+
71
+ foreach ($commands as $command) {
72
+ $response = $connection->readResponse($command);
73
+
74
+ if ($response instanceof ErrorResponseInterface) {
75
+ $connection->executeCommand($profile->createCommand('discard'));
76
+ throw new ServerException($response->getMessage());
77
+ }
78
+ }
79
+
80
+ $executed = $connection->executeCommand($profile->createCommand('exec'));
81
+
82
+ if (!isset($executed)) {
83
+ // TODO: should be throwing a more appropriate exception.
84
+ throw new ClientException(
85
+ 'The underlying transaction has been aborted by the server.'
86
+ );
87
+ }
88
+
89
+ if (count($executed) !== count($commands)) {
90
+ $expected = count($commands);
91
+ $received = count($executed);
92
+
93
+ throw new ClientException(
94
+ "Invalid number of responses [expected $expected, received $received]."
95
+ );
96
+ }
97
+
98
+ $responses = array();
99
+ $sizeOfPipe = count($commands);
100
+ $exceptions = $this->throwServerExceptions();
101
+
102
+ for ($i = 0; $i < $sizeOfPipe; ++$i) {
103
+ $command = $commands->dequeue();
104
+ $response = $executed[$i];
105
+
106
+ if (!$response instanceof ResponseInterface) {
107
+ $responses[] = $command->parseResponse($response);
108
+ } elseif ($response instanceof ErrorResponseInterface && $exceptions) {
109
+ $this->exception($connection, $response);
110
+ } else {
111
+ $responses[] = $response;
112
+ }
113
+
114
+ unset($executed[$i]);
115
+ }
116
+
117
+ return $responses;
118
+ }
119
+ }
includes/predis/src/Pipeline/ConnectionErrorProof.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Pipeline;
13
+
14
+ use Predis\CommunicationException;
15
+ use Predis\Connection\Aggregate\ClusterInterface;
16
+ use Predis\Connection\ConnectionInterface;
17
+ use Predis\Connection\NodeConnectionInterface;
18
+ use Predis\NotSupportedException;
19
+
20
+ /**
21
+ * Command pipeline that does not throw exceptions on connection errors, but
22
+ * returns the exception instances as the rest of the response elements.
23
+ *
24
+ * @todo Awful naming!
25
+ *
26
+ * @author Daniele Alessandri <suppakilla@gmail.com>
27
+ */
28
+ class ConnectionErrorProof extends Pipeline
29
+ {
30
+ /**
31
+ * {@inheritdoc}
32
+ */
33
+ protected function getConnection()
34
+ {
35
+ return $this->getClient()->getConnection();
36
+ }
37
+
38
+ /**
39
+ * {@inheritdoc}
40
+ */
41
+ protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
42
+ {
43
+ if ($connection instanceof NodeConnectionInterface) {
44
+ return $this->executeSingleNode($connection, $commands);
45
+ } elseif ($connection instanceof ClusterInterface) {
46
+ return $this->executeCluster($connection, $commands);
47
+ } else {
48
+ $class = get_class($connection);
49
+
50
+ throw new NotSupportedException("The connection class '$class' is not supported.");
51
+ }
52
+ }
53
+
54
+ /**
55
+ * {@inheritdoc}
56
+ */
57
+ protected function executeSingleNode(NodeConnectionInterface $connection, \SplQueue $commands)
58
+ {
59
+ $responses = array();
60
+ $sizeOfPipe = count($commands);
61
+
62
+ foreach ($commands as $command) {
63
+ try {
64
+ $connection->writeRequest($command);
65
+ } catch (CommunicationException $exception) {
66
+ return array_fill(0, $sizeOfPipe, $exception);
67
+ }
68
+ }
69
+
70
+ for ($i = 0; $i < $sizeOfPipe; ++$i) {
71
+ $command = $commands->dequeue();
72
+
73
+ try {
74
+ $responses[$i] = $connection->readResponse($command);
75
+ } catch (CommunicationException $exception) {
76
+ $add = count($commands) - count($responses);
77
+ $responses = array_merge($responses, array_fill(0, $add, $exception));
78
+
79
+ break;
80
+ }
81
+ }
82
+
83
+ return $responses;
84
+ }
85
+
86
+ /**
87
+ * {@inheritdoc}
88
+ */
89
+ protected function executeCluster(ClusterInterface $connection, \SplQueue $commands)
90
+ {
91
+ $responses = array();
92
+ $sizeOfPipe = count($commands);
93
+ $exceptions = array();
94
+
95
+ foreach ($commands as $command) {
96
+ $cmdConnection = $connection->getConnection($command);
97
+
98
+ if (isset($exceptions[spl_object_hash($cmdConnection)])) {
99
+ continue;
100
+ }
101
+
102
+ try {
103
+ $cmdConnection->writeRequest($command);
104
+ } catch (CommunicationException $exception) {
105
+ $exceptions[spl_object_hash($cmdConnection)] = $exception;
106
+ }
107
+ }
108
+
109
+ for ($i = 0; $i < $sizeOfPipe; ++$i) {
110
+ $command = $commands->dequeue();
111
+
112
+ $cmdConnection = $connection->getConnection($command);
113
+ $connectionHash = spl_object_hash($cmdConnection);
114
+
115
+ if (isset($exceptions[$connectionHash])) {
116
+ $responses[$i] = $exceptions[$connectionHash];
117
+ continue;
118
+ }
119
+
120
+ try {
121
+ $responses[$i] = $cmdConnection->readResponse($command);
122
+ } catch (CommunicationException $exception) {
123
+ $responses[$i] = $exception;
124
+ $exceptions[$connectionHash] = $exception;
125
+ }
126
+ }
127
+
128
+ return $responses;
129
+ }
130
+ }
includes/predis/src/Pipeline/FireAndForget.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Pipeline;
13
+
14
+ use Predis\Connection\ConnectionInterface;
15
+
16
+ /**
17
+ * Command pipeline that writes commands to the servers but discards responses.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ class FireAndForget extends Pipeline
22
+ {
23
+ /**
24
+ * {@inheritdoc}
25
+ */
26
+ protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
27
+ {
28
+ while (!$commands->isEmpty()) {
29
+ $connection->writeRequest($commands->dequeue());
30
+ }
31
+
32
+ $connection->disconnect();
33
+
34
+ return array();
35
+ }
36
+ }
includes/predis/src/Pipeline/Pipeline.php ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Pipeline;
13
+
14
+ use Predis\ClientContextInterface;
15
+ use Predis\ClientException;
16
+ use Predis\ClientInterface;
17
+ use Predis\Command\CommandInterface;
18
+ use Predis\Connection\Aggregate\ReplicationInterface;
19
+ use Predis\Connection\ConnectionInterface;
20
+ use Predis\Response\ErrorInterface as ErrorResponseInterface;
21
+ use Predis\Response\ResponseInterface;
22
+ use Predis\Response\ServerException;
23
+
24
+ /**
25
+ * Implementation of a command pipeline in which write and read operations of
26
+ * Redis commands are pipelined to alleviate the effects of network round-trips.
27
+ *
28
+ * {@inheritdoc}
29
+ *
30
+ * @author Daniele Alessandri <suppakilla@gmail.com>
31
+ */
32
+ class Pipeline implements ClientContextInterface
33
+ {
34
+ private $client;
35
+ private $pipeline;
36
+
37
+ private $responses = array();
38
+ private $running = false;
39
+
40
+ /**
41
+ * @param ClientInterface $client Client instance used by the context.
42
+ */
43
+ public function __construct(ClientInterface $client)
44
+ {
45
+ $this->client = $client;
46
+ $this->pipeline = new \SplQueue();
47
+ }
48
+
49
+ /**
50
+ * Queues a command into the pipeline buffer.
51
+ *
52
+ * @param string $method Command ID.
53
+ * @param array $arguments Arguments for the command.
54
+ *
55
+ * @return $this
56
+ */
57
+ public function __call($method, $arguments)
58
+ {
59
+ $command = $this->client->createCommand($method, $arguments);
60
+ $this->recordCommand($command);
61
+
62
+ return $this;
63
+ }
64
+
65
+ /**
66
+ * Queues a command instance into the pipeline buffer.
67
+ *
68
+ * @param CommandInterface $command Command to be queued in the buffer.
69
+ */
70
+ protected function recordCommand(CommandInterface $command)
71
+ {
72
+ $this->pipeline->enqueue($command);
73
+ }
74
+
75
+ /**
76
+ * Queues a command instance into the pipeline buffer.
77
+ *
78
+ * @param CommandInterface $command Command instance to be queued in the buffer.
79
+ *
80
+ * @return $this
81
+ */
82
+ public function executeCommand(CommandInterface $command)
83
+ {
84
+ $this->recordCommand($command);
85
+
86
+ return $this;
87
+ }
88
+
89
+ /**
90
+ * Throws an exception on -ERR responses returned by Redis.
91
+ *
92
+ * @param ConnectionInterface $connection Redis connection that returned the error.
93
+ * @param ErrorResponseInterface $response Instance of the error response.
94
+ *
95
+ * @throws ServerException
96
+ */
97
+ protected function exception(ConnectionInterface $connection, ErrorResponseInterface $response)
98
+ {
99
+ $connection->disconnect();
100
+ $message = $response->getMessage();
101
+
102
+ throw new ServerException($message);
103
+ }
104
+
105
+ /**
106
+ * Returns the underlying connection to be used by the pipeline.
107
+ *
108
+ * @return ConnectionInterface
109
+ */
110
+ protected function getConnection()
111
+ {
112
+ $connection = $this->getClient()->getConnection();
113
+
114
+ if ($connection instanceof ReplicationInterface) {
115
+ $connection->switchTo('master');
116
+ }
117
+
118
+ return $connection;
119
+ }
120
+
121
+ /**
122
+ * Implements the logic to flush the queued commands and read the responses
123
+ * from the current connection.
124
+ *
125
+ * @param ConnectionInterface $connection Current connection instance.
126
+ * @param \SplQueue $commands Queued commands.
127
+ *
128
+ * @return array
129
+ */
130
+ protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
131
+ {
132
+ foreach ($commands as $command) {
133
+ $connection->writeRequest($command);
134
+ }
135
+
136
+ $responses = array();
137
+ $exceptions = $this->throwServerExceptions();
138
+
139
+ while (!$commands->isEmpty()) {
140
+ $command = $commands->dequeue();
141
+ $response = $connection->readResponse($command);
142
+
143
+ if (!$response instanceof ResponseInterface) {
144
+ $responses[] = $command->parseResponse($response);
145
+ } elseif ($response instanceof ErrorResponseInterface && $exceptions) {
146
+ $this->exception($connection, $response);
147
+ } else {
148
+ $responses[] = $response;
149
+ }
150
+ }
151
+
152
+ return $responses;
153
+ }
154
+
155
+ /**
156
+ * Flushes the buffer holding all of the commands queued so far.
157
+ *
158
+ * @param bool $send Specifies if the commands in the buffer should be sent to Redis.
159
+ *
160
+ * @return $this
161
+ */
162
+ public function flushPipeline($send = true)
163
+ {
164
+ if ($send && !$this->pipeline->isEmpty()) {
165
+ $responses = $this->executePipeline($this->getConnection(), $this->pipeline);
166
+ $this->responses = array_merge($this->responses, $responses);
167
+ } else {
168
+ $this->pipeline = new \SplQueue();
169
+ }
170
+
171
+ return $this;
172
+ }
173
+
174
+ /**
175
+ * Marks the running status of the pipeline.
176
+ *
177
+ * @param bool $bool Sets the running status of the pipeline.
178
+ *
179
+ * @throws ClientException
180
+ */
181
+ private function setRunning($bool)
182
+ {
183
+ if ($bool && $this->running) {
184
+ throw new ClientException('The current pipeline context is already being executed.');
185
+ }
186
+
187
+ $this->running = $bool;
188
+ }
189
+
190
+ /**
191
+ * Handles the actual execution of the whole pipeline.
192
+ *
193
+ * @param mixed $callable Optional callback for execution.
194
+ *
195
+ * @throws \Exception
196
+ * @throws \InvalidArgumentException
197
+ *
198
+ * @return array
199
+ */
200
+ public function execute($callable = null)
201
+ {
202
+ if ($callable && !is_callable($callable)) {
203
+ throw new \InvalidArgumentException('The argument must be a callable object.');
204
+ }
205
+
206
+ $exception = null;
207
+ $this->setRunning(true);
208
+
209
+ try {
210
+ if ($callable) {
211
+ call_user_func($callable, $this);
212
+ }
213
+
214
+ $this->flushPipeline();
215
+ } catch (\Exception $exception) {
216
+ // NOOP
217
+ }
218
+
219
+ $this->setRunning(false);
220
+
221
+ if ($exception) {
222
+ throw $exception;
223
+ }
224
+
225
+ return $this->responses;
226
+ }
227
+
228
+ /**
229
+ * Returns if the pipeline should throw exceptions on server errors.
230
+ *
231
+ * @return bool
232
+ */
233
+ protected function throwServerExceptions()
234
+ {
235
+ return (bool) $this->client->getOptions()->exceptions;
236
+ }
237
+
238
+ /**
239
+ * Returns the underlying client instance used by the pipeline object.
240
+ *
241
+ * @return ClientInterface
242
+ */
243
+ public function getClient()
244
+ {
245
+ return $this->client;
246
+ }
247
+ }
includes/predis/src/PredisException.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis;
13
+
14
+ /**
15
+ * Base exception class for Predis-related errors.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ abstract class PredisException extends \Exception
20
+ {
21
+ }
includes/predis/src/Profile/Factory.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ use Predis\ClientException;
15
+
16
+ /**
17
+ * Factory class for creating profile instances from strings.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ final class Factory
22
+ {
23
+ private static $profiles = array(
24
+ '2.0' => 'Predis\Profile\RedisVersion200',
25
+ '2.2' => 'Predis\Profile\RedisVersion220',
26
+ '2.4' => 'Predis\Profile\RedisVersion240',
27
+ '2.6' => 'Predis\Profile\RedisVersion260',
28
+ '2.8' => 'Predis\Profile\RedisVersion280',
29
+ '3.0' => 'Predis\Profile\RedisVersion300',
30
+ '3.2' => 'Predis\Profile\RedisVersion320',
31
+ 'dev' => 'Predis\Profile\RedisUnstable',
32
+ 'default' => 'Predis\Profile\RedisVersion320',
33
+ );
34
+
35
+ /**
36
+ *
37
+ */
38
+ private function __construct()
39
+ {
40
+ // NOOP
41
+ }
42
+
43
+ /**
44
+ * Returns the default server profile.
45
+ *
46
+ * @return ProfileInterface
47
+ */
48
+ public static function getDefault()
49
+ {
50
+ return self::get('default');
51
+ }
52
+
53
+ /**
54
+ * Returns the development server profile.
55
+ *
56
+ * @return ProfileInterface
57
+ */
58
+ public static function getDevelopment()
59
+ {
60
+ return self::get('dev');
61
+ }
62
+
63
+ /**
64
+ * Registers a new server profile.
65
+ *
66
+ * @param string $alias Profile version or alias.
67
+ * @param string $class FQN of a class implementing Predis\Profile\ProfileInterface.
68
+ *
69
+ * @throws \InvalidArgumentException
70
+ */
71
+ public static function define($alias, $class)
72
+ {
73
+ $reflection = new \ReflectionClass($class);
74
+
75
+ if (!$reflection->isSubclassOf('Predis\Profile\ProfileInterface')) {
76
+ throw new \InvalidArgumentException("The class '$class' is not a valid profile class.");
77
+ }
78
+
79
+ self::$profiles[$alias] = $class;
80
+ }
81
+
82
+ /**
83
+ * Returns the specified server profile.
84
+ *
85
+ * @param string $version Profile version or alias.
86
+ *
87
+ * @throws ClientException
88
+ *
89
+ * @return ProfileInterface
90
+ */
91
+ public static function get($version)
92
+ {
93
+ if (!isset(self::$profiles[$version])) {
94
+ throw new ClientException("Unknown server profile: '$version'.");
95
+ }
96
+
97
+ $profile = self::$profiles[$version];
98
+
99
+ return new $profile();
100
+ }
101
+ }
includes/predis/src/Profile/ProfileInterface.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * A profile defines all the features and commands supported by certain versions
18
+ * of Redis. Instances of Predis\Client should use a server profile matching the
19
+ * version of Redis being used.
20
+ *
21
+ * @author Daniele Alessandri <suppakilla@gmail.com>
22
+ */
23
+ interface ProfileInterface
24
+ {
25
+ /**
26
+ * Returns the profile version corresponding to the Redis version.
27
+ *
28
+ * @return string
29
+ */
30
+ public function getVersion();
31
+
32
+ /**
33
+ * Checks if the profile supports the specified command.
34
+ *
35
+ * @param string $commandID Command ID.
36
+ *
37
+ * @return bool
38
+ */
39
+ public function supportsCommand($commandID);
40
+
41
+ /**
42
+ * Checks if the profile supports the specified list of commands.
43
+ *
44
+ * @param array $commandIDs List of command IDs.
45
+ *
46
+ * @return string
47
+ */
48
+ public function supportsCommands(array $commandIDs);
49
+
50
+ /**
51
+ * Creates a new command instance.
52
+ *
53
+ * @param string $commandID Command ID.
54
+ * @param array $arguments Arguments for the command.
55
+ *
56
+ * @return CommandInterface
57
+ */
58
+ public function createCommand($commandID, array $arguments = array());
59
+ }
includes/predis/src/Profile/RedisProfile.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ use Predis\ClientException;
15
+ use Predis\Command\Processor\ProcessorInterface;
16
+
17
+ /**
18
+ * Base class implementing common functionalities for Redis server profiles.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ abstract class RedisProfile implements ProfileInterface
23
+ {
24
+ private $commands;
25
+ private $processor;
26
+
27
+ /**
28
+ *
29
+ */
30
+ public function __construct()
31
+ {
32
+ $this->commands = $this->getSupportedCommands();
33
+ }
34
+
35
+ /**
36
+ * Returns a map of all the commands supported by the profile and their
37
+ * actual PHP classes.
38
+ *
39
+ * @return array
40
+ */
41
+ abstract protected function getSupportedCommands();
42
+
43
+ /**
44
+ * {@inheritdoc}
45
+ */
46
+ public function supportsCommand($commandID)
47
+ {
48
+ return isset($this->commands[strtoupper($commandID)]);
49
+ }
50
+
51
+ /**
52
+ * {@inheritdoc}
53
+ */
54
+ public function supportsCommands(array $commandIDs)
55
+ {
56
+ foreach ($commandIDs as $commandID) {
57
+ if (!$this->supportsCommand($commandID)) {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * Returns the fully-qualified name of a class representing the specified
67
+ * command ID registered in the current server profile.
68
+ *
69
+ * @param string $commandID Command ID.
70
+ *
71
+ * @return string|null
72
+ */
73
+ public function getCommandClass($commandID)
74
+ {
75
+ if (isset($this->commands[$commandID = strtoupper($commandID)])) {
76
+ return $this->commands[$commandID];
77
+ }
78
+ }
79
+
80
+ /**
81
+ * {@inheritdoc}
82
+ */
83
+ public function createCommand($commandID, array $arguments = array())
84
+ {
85
+ $commandID = strtoupper($commandID);
86
+
87
+ if (!isset($this->commands[$commandID])) {
88
+ throw new ClientException("Command '$commandID' is not a registered Redis command.");
89
+ }
90
+
91
+ $commandClass = $this->commands[$commandID];
92
+ $command = new $commandClass();
93
+ $command->setArguments($arguments);
94
+
95
+ if (isset($this->processor)) {
96
+ $this->processor->process($command);
97
+ }
98
+
99
+ return $command;
100
+ }
101
+
102
+ /**
103
+ * Defines a new command in the server profile.
104
+ *
105
+ * @param string $commandID Command ID.
106
+ * @param string $class Fully-qualified name of a Predis\Command\CommandInterface.
107
+ *
108
+ * @throws \InvalidArgumentException
109
+ */
110
+ public function defineCommand($commandID, $class)
111
+ {
112
+ $reflection = new \ReflectionClass($class);
113
+
114
+ if (!$reflection->isSubclassOf('Predis\Command\CommandInterface')) {
115
+ throw new \InvalidArgumentException("The class '$class' is not a valid command class.");
116
+ }
117
+
118
+ $this->commands[strtoupper($commandID)] = $class;
119
+ }
120
+
121
+ /**
122
+ * {@inheritdoc}
123
+ */
124
+ public function setProcessor(ProcessorInterface $processor = null)
125
+ {
126
+ $this->processor = $processor;
127
+ }
128
+
129
+ /**
130
+ * {@inheritdoc}
131
+ */
132
+ public function getProcessor()
133
+ {
134
+ return $this->processor;
135
+ }
136
+
137
+ /**
138
+ * Returns the version of server profile as its string representation.
139
+ *
140
+ * @return string
141
+ */
142
+ public function __toString()
143
+ {
144
+ return $this->getVersion();
145
+ }
146
+ }
includes/predis/src/Profile/RedisUnstable.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for the current unstable version of Redis.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisUnstable extends RedisVersion320
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '3.2';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array_merge(parent::getSupportedCommands(), array(
35
+ // EMPTY
36
+ ));
37
+ }
38
+ }
includes/predis/src/Profile/RedisVersion200.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for Redis 2.0.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisVersion200 extends RedisProfile
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '2.0';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+
37
+ /* commands operating on the key space */
38
+ 'EXISTS' => 'Predis\Command\KeyExists',
39
+ 'DEL' => 'Predis\Command\KeyDelete',
40
+ 'TYPE' => 'Predis\Command\KeyType',
41
+ 'KEYS' => 'Predis\Command\KeyKeys',
42
+ 'RANDOMKEY' => 'Predis\Command\KeyRandom',
43
+ 'RENAME' => 'Predis\Command\KeyRename',
44
+ 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
45
+ 'EXPIRE' => 'Predis\Command\KeyExpire',
46
+ 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
47
+ 'TTL' => 'Predis\Command\KeyTimeToLive',
48
+ 'MOVE' => 'Predis\Command\KeyMove',
49
+ 'SORT' => 'Predis\Command\KeySort',
50
+
51
+ /* commands operating on string values */
52
+ 'SET' => 'Predis\Command\StringSet',
53
+ 'SETNX' => 'Predis\Command\StringSetPreserve',
54
+ 'MSET' => 'Predis\Command\StringSetMultiple',
55
+ 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
56
+ 'GET' => 'Predis\Command\StringGet',
57
+ 'MGET' => 'Predis\Command\StringGetMultiple',
58
+ 'GETSET' => 'Predis\Command\StringGetSet',
59
+ 'INCR' => 'Predis\Command\StringIncrement',
60
+ 'INCRBY' => 'Predis\Command\StringIncrementBy',
61
+ 'DECR' => 'Predis\Command\StringDecrement',
62
+ 'DECRBY' => 'Predis\Command\StringDecrementBy',
63
+
64
+ /* commands operating on lists */
65
+ 'RPUSH' => 'Predis\Command\ListPushTail',
66
+ 'LPUSH' => 'Predis\Command\ListPushHead',
67
+ 'LLEN' => 'Predis\Command\ListLength',
68
+ 'LRANGE' => 'Predis\Command\ListRange',
69
+ 'LTRIM' => 'Predis\Command\ListTrim',
70
+ 'LINDEX' => 'Predis\Command\ListIndex',
71
+ 'LSET' => 'Predis\Command\ListSet',
72
+ 'LREM' => 'Predis\Command\ListRemove',
73
+ 'LPOP' => 'Predis\Command\ListPopFirst',
74
+ 'RPOP' => 'Predis\Command\ListPopLast',
75
+ 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
76
+
77
+ /* commands operating on sets */
78
+ 'SADD' => 'Predis\Command\SetAdd',
79
+ 'SREM' => 'Predis\Command\SetRemove',
80
+ 'SPOP' => 'Predis\Command\SetPop',
81
+ 'SMOVE' => 'Predis\Command\SetMove',
82
+ 'SCARD' => 'Predis\Command\SetCardinality',
83
+ 'SISMEMBER' => 'Predis\Command\SetIsMember',
84
+ 'SINTER' => 'Predis\Command\SetIntersection',
85
+ 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
86
+ 'SUNION' => 'Predis\Command\SetUnion',
87
+ 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
88
+ 'SDIFF' => 'Predis\Command\SetDifference',
89
+ 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
90
+ 'SMEMBERS' => 'Predis\Command\SetMembers',
91
+ 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
92
+
93
+ /* commands operating on sorted sets */
94
+ 'ZADD' => 'Predis\Command\ZSetAdd',
95
+ 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
96
+ 'ZREM' => 'Predis\Command\ZSetRemove',
97
+ 'ZRANGE' => 'Predis\Command\ZSetRange',
98
+ 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
99
+ 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
100
+ 'ZCARD' => 'Predis\Command\ZSetCardinality',
101
+ 'ZSCORE' => 'Predis\Command\ZSetScore',
102
+ 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
103
+
104
+ /* connection related commands */
105
+ 'PING' => 'Predis\Command\ConnectionPing',
106
+ 'AUTH' => 'Predis\Command\ConnectionAuth',
107
+ 'SELECT' => 'Predis\Command\ConnectionSelect',
108
+ 'ECHO' => 'Predis\Command\ConnectionEcho',
109
+ 'QUIT' => 'Predis\Command\ConnectionQuit',
110
+
111
+ /* remote server control commands */
112
+ 'INFO' => 'Predis\Command\ServerInfo',
113
+ 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
114
+ 'MONITOR' => 'Predis\Command\ServerMonitor',
115
+ 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
116
+ 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
117
+ 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
118
+ 'SAVE' => 'Predis\Command\ServerSave',
119
+ 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
120
+ 'LASTSAVE' => 'Predis\Command\ServerLastSave',
121
+ 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
122
+ 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
123
+
124
+ /* ---------------- Redis 2.0 ---------------- */
125
+
126
+ /* commands operating on string values */
127
+ 'SETEX' => 'Predis\Command\StringSetExpire',
128
+ 'APPEND' => 'Predis\Command\StringAppend',
129
+ 'SUBSTR' => 'Predis\Command\StringSubstr',
130
+
131
+ /* commands operating on lists */
132
+ 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
133
+ 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
134
+
135
+ /* commands operating on sorted sets */
136
+ 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
137
+ 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
138
+ 'ZCOUNT' => 'Predis\Command\ZSetCount',
139
+ 'ZRANK' => 'Predis\Command\ZSetRank',
140
+ 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
141
+ 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
142
+
143
+ /* commands operating on hashes */
144
+ 'HSET' => 'Predis\Command\HashSet',
145
+ 'HSETNX' => 'Predis\Command\HashSetPreserve',
146
+ 'HMSET' => 'Predis\Command\HashSetMultiple',
147
+ 'HINCRBY' => 'Predis\Command\HashIncrementBy',
148
+ 'HGET' => 'Predis\Command\HashGet',
149
+ 'HMGET' => 'Predis\Command\HashGetMultiple',
150
+ 'HDEL' => 'Predis\Command\HashDelete',
151
+ 'HEXISTS' => 'Predis\Command\HashExists',
152
+ 'HLEN' => 'Predis\Command\HashLength',
153
+ 'HKEYS' => 'Predis\Command\HashKeys',
154
+ 'HVALS' => 'Predis\Command\HashValues',
155
+ 'HGETALL' => 'Predis\Command\HashGetAll',
156
+
157
+ /* transactions */
158
+ 'MULTI' => 'Predis\Command\TransactionMulti',
159
+ 'EXEC' => 'Predis\Command\TransactionExec',
160
+ 'DISCARD' => 'Predis\Command\TransactionDiscard',
161
+
162
+ /* publish - subscribe */
163
+ 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
164
+ 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
165
+ 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
166
+ 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
167
+ 'PUBLISH' => 'Predis\Command\PubSubPublish',
168
+
169
+ /* remote server control commands */
170
+ 'CONFIG' => 'Predis\Command\ServerConfig',
171
+ );
172
+ }
173
+ }
includes/predis/src/Profile/RedisVersion220.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for Redis 2.2.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisVersion220 extends RedisProfile
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '2.2';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+
37
+ /* commands operating on the key space */
38
+ 'EXISTS' => 'Predis\Command\KeyExists',
39
+ 'DEL' => 'Predis\Command\KeyDelete',
40
+ 'TYPE' => 'Predis\Command\KeyType',
41
+ 'KEYS' => 'Predis\Command\KeyKeys',
42
+ 'RANDOMKEY' => 'Predis\Command\KeyRandom',
43
+ 'RENAME' => 'Predis\Command\KeyRename',
44
+ 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
45
+ 'EXPIRE' => 'Predis\Command\KeyExpire',
46
+ 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
47
+ 'TTL' => 'Predis\Command\KeyTimeToLive',
48
+ 'MOVE' => 'Predis\Command\KeyMove',
49
+ 'SORT' => 'Predis\Command\KeySort',
50
+
51
+ /* commands operating on string values */
52
+ 'SET' => 'Predis\Command\StringSet',
53
+ 'SETNX' => 'Predis\Command\StringSetPreserve',
54
+ 'MSET' => 'Predis\Command\StringSetMultiple',
55
+ 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
56
+ 'GET' => 'Predis\Command\StringGet',
57
+ 'MGET' => 'Predis\Command\StringGetMultiple',
58
+ 'GETSET' => 'Predis\Command\StringGetSet',
59
+ 'INCR' => 'Predis\Command\StringIncrement',
60
+ 'INCRBY' => 'Predis\Command\StringIncrementBy',
61
+ 'DECR' => 'Predis\Command\StringDecrement',
62
+ 'DECRBY' => 'Predis\Command\StringDecrementBy',
63
+
64
+ /* commands operating on lists */
65
+ 'RPUSH' => 'Predis\Command\ListPushTail',
66
+ 'LPUSH' => 'Predis\Command\ListPushHead',
67
+ 'LLEN' => 'Predis\Command\ListLength',
68
+ 'LRANGE' => 'Predis\Command\ListRange',
69
+ 'LTRIM' => 'Predis\Command\ListTrim',
70
+ 'LINDEX' => 'Predis\Command\ListIndex',
71
+ 'LSET' => 'Predis\Command\ListSet',
72
+ 'LREM' => 'Predis\Command\ListRemove',
73
+ 'LPOP' => 'Predis\Command\ListPopFirst',
74
+ 'RPOP' => 'Predis\Command\ListPopLast',
75
+ 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
76
+
77
+ /* commands operating on sets */
78
+ 'SADD' => 'Predis\Command\SetAdd',
79
+ 'SREM' => 'Predis\Command\SetRemove',
80
+ 'SPOP' => 'Predis\Command\SetPop',
81
+ 'SMOVE' => 'Predis\Command\SetMove',
82
+ 'SCARD' => 'Predis\Command\SetCardinality',
83
+ 'SISMEMBER' => 'Predis\Command\SetIsMember',
84
+ 'SINTER' => 'Predis\Command\SetIntersection',
85
+ 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
86
+ 'SUNION' => 'Predis\Command\SetUnion',
87
+ 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
88
+ 'SDIFF' => 'Predis\Command\SetDifference',
89
+ 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
90
+ 'SMEMBERS' => 'Predis\Command\SetMembers',
91
+ 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
92
+
93
+ /* commands operating on sorted sets */
94
+ 'ZADD' => 'Predis\Command\ZSetAdd',
95
+ 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
96
+ 'ZREM' => 'Predis\Command\ZSetRemove',
97
+ 'ZRANGE' => 'Predis\Command\ZSetRange',
98
+ 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
99
+ 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
100
+ 'ZCARD' => 'Predis\Command\ZSetCardinality',
101
+ 'ZSCORE' => 'Predis\Command\ZSetScore',
102
+ 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
103
+
104
+ /* connection related commands */
105
+ 'PING' => 'Predis\Command\ConnectionPing',
106
+ 'AUTH' => 'Predis\Command\ConnectionAuth',
107
+ 'SELECT' => 'Predis\Command\ConnectionSelect',
108
+ 'ECHO' => 'Predis\Command\ConnectionEcho',
109
+ 'QUIT' => 'Predis\Command\ConnectionQuit',
110
+
111
+ /* remote server control commands */
112
+ 'INFO' => 'Predis\Command\ServerInfo',
113
+ 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
114
+ 'MONITOR' => 'Predis\Command\ServerMonitor',
115
+ 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
116
+ 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
117
+ 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
118
+ 'SAVE' => 'Predis\Command\ServerSave',
119
+ 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
120
+ 'LASTSAVE' => 'Predis\Command\ServerLastSave',
121
+ 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
122
+ 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
123
+
124
+ /* ---------------- Redis 2.0 ---------------- */
125
+
126
+ /* commands operating on string values */
127
+ 'SETEX' => 'Predis\Command\StringSetExpire',
128
+ 'APPEND' => 'Predis\Command\StringAppend',
129
+ 'SUBSTR' => 'Predis\Command\StringSubstr',
130
+
131
+ /* commands operating on lists */
132
+ 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
133
+ 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
134
+
135
+ /* commands operating on sorted sets */
136
+ 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
137
+ 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
138
+ 'ZCOUNT' => 'Predis\Command\ZSetCount',
139
+ 'ZRANK' => 'Predis\Command\ZSetRank',
140
+ 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
141
+ 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
142
+
143
+ /* commands operating on hashes */
144
+ 'HSET' => 'Predis\Command\HashSet',
145
+ 'HSETNX' => 'Predis\Command\HashSetPreserve',
146
+ 'HMSET' => 'Predis\Command\HashSetMultiple',
147
+ 'HINCRBY' => 'Predis\Command\HashIncrementBy',
148
+ 'HGET' => 'Predis\Command\HashGet',
149
+ 'HMGET' => 'Predis\Command\HashGetMultiple',
150
+ 'HDEL' => 'Predis\Command\HashDelete',
151
+ 'HEXISTS' => 'Predis\Command\HashExists',
152
+ 'HLEN' => 'Predis\Command\HashLength',
153
+ 'HKEYS' => 'Predis\Command\HashKeys',
154
+ 'HVALS' => 'Predis\Command\HashValues',
155
+ 'HGETALL' => 'Predis\Command\HashGetAll',
156
+
157
+ /* transactions */
158
+ 'MULTI' => 'Predis\Command\TransactionMulti',
159
+ 'EXEC' => 'Predis\Command\TransactionExec',
160
+ 'DISCARD' => 'Predis\Command\TransactionDiscard',
161
+
162
+ /* publish - subscribe */
163
+ 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
164
+ 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
165
+ 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
166
+ 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
167
+ 'PUBLISH' => 'Predis\Command\PubSubPublish',
168
+
169
+ /* remote server control commands */
170
+ 'CONFIG' => 'Predis\Command\ServerConfig',
171
+
172
+ /* ---------------- Redis 2.2 ---------------- */
173
+
174
+ /* commands operating on the key space */
175
+ 'PERSIST' => 'Predis\Command\KeyPersist',
176
+
177
+ /* commands operating on string values */
178
+ 'STRLEN' => 'Predis\Command\StringStrlen',
179
+ 'SETRANGE' => 'Predis\Command\StringSetRange',
180
+ 'GETRANGE' => 'Predis\Command\StringGetRange',
181
+ 'SETBIT' => 'Predis\Command\StringSetBit',
182
+ 'GETBIT' => 'Predis\Command\StringGetBit',
183
+
184
+ /* commands operating on lists */
185
+ 'RPUSHX' => 'Predis\Command\ListPushTailX',
186
+ 'LPUSHX' => 'Predis\Command\ListPushHeadX',
187
+ 'LINSERT' => 'Predis\Command\ListInsert',
188
+ 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
189
+
190
+ /* commands operating on sorted sets */
191
+ 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
192
+
193
+ /* transactions */
194
+ 'WATCH' => 'Predis\Command\TransactionWatch',
195
+ 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
196
+
197
+ /* remote server control commands */
198
+ 'OBJECT' => 'Predis\Command\ServerObject',
199
+ 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
200
+ );
201
+ }
202
+ }
includes/predis/src/Profile/RedisVersion240.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for Redis 2.4.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisVersion240 extends RedisProfile
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '2.4';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+
37
+ /* commands operating on the key space */
38
+ 'EXISTS' => 'Predis\Command\KeyExists',
39
+ 'DEL' => 'Predis\Command\KeyDelete',
40
+ 'TYPE' => 'Predis\Command\KeyType',
41
+ 'KEYS' => 'Predis\Command\KeyKeys',
42
+ 'RANDOMKEY' => 'Predis\Command\KeyRandom',
43
+ 'RENAME' => 'Predis\Command\KeyRename',
44
+ 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
45
+ 'EXPIRE' => 'Predis\Command\KeyExpire',
46
+ 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
47
+ 'TTL' => 'Predis\Command\KeyTimeToLive',
48
+ 'MOVE' => 'Predis\Command\KeyMove',
49
+ 'SORT' => 'Predis\Command\KeySort',
50
+
51
+ /* commands operating on string values */
52
+ 'SET' => 'Predis\Command\StringSet',
53
+ 'SETNX' => 'Predis\Command\StringSetPreserve',
54
+ 'MSET' => 'Predis\Command\StringSetMultiple',
55
+ 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
56
+ 'GET' => 'Predis\Command\StringGet',
57
+ 'MGET' => 'Predis\Command\StringGetMultiple',
58
+ 'GETSET' => 'Predis\Command\StringGetSet',
59
+ 'INCR' => 'Predis\Command\StringIncrement',
60
+ 'INCRBY' => 'Predis\Command\StringIncrementBy',
61
+ 'DECR' => 'Predis\Command\StringDecrement',
62
+ 'DECRBY' => 'Predis\Command\StringDecrementBy',
63
+
64
+ /* commands operating on lists */
65
+ 'RPUSH' => 'Predis\Command\ListPushTail',
66
+ 'LPUSH' => 'Predis\Command\ListPushHead',
67
+ 'LLEN' => 'Predis\Command\ListLength',
68
+ 'LRANGE' => 'Predis\Command\ListRange',
69
+ 'LTRIM' => 'Predis\Command\ListTrim',
70
+ 'LINDEX' => 'Predis\Command\ListIndex',
71
+ 'LSET' => 'Predis\Command\ListSet',
72
+ 'LREM' => 'Predis\Command\ListRemove',
73
+ 'LPOP' => 'Predis\Command\ListPopFirst',
74
+ 'RPOP' => 'Predis\Command\ListPopLast',
75
+ 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
76
+
77
+ /* commands operating on sets */
78
+ 'SADD' => 'Predis\Command\SetAdd',
79
+ 'SREM' => 'Predis\Command\SetRemove',
80
+ 'SPOP' => 'Predis\Command\SetPop',
81
+ 'SMOVE' => 'Predis\Command\SetMove',
82
+ 'SCARD' => 'Predis\Command\SetCardinality',
83
+ 'SISMEMBER' => 'Predis\Command\SetIsMember',
84
+ 'SINTER' => 'Predis\Command\SetIntersection',
85
+ 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
86
+ 'SUNION' => 'Predis\Command\SetUnion',
87
+ 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
88
+ 'SDIFF' => 'Predis\Command\SetDifference',
89
+ 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
90
+ 'SMEMBERS' => 'Predis\Command\SetMembers',
91
+ 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
92
+
93
+ /* commands operating on sorted sets */
94
+ 'ZADD' => 'Predis\Command\ZSetAdd',
95
+ 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
96
+ 'ZREM' => 'Predis\Command\ZSetRemove',
97
+ 'ZRANGE' => 'Predis\Command\ZSetRange',
98
+ 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
99
+ 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
100
+ 'ZCARD' => 'Predis\Command\ZSetCardinality',
101
+ 'ZSCORE' => 'Predis\Command\ZSetScore',
102
+ 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
103
+
104
+ /* connection related commands */
105
+ 'PING' => 'Predis\Command\ConnectionPing',
106
+ 'AUTH' => 'Predis\Command\ConnectionAuth',
107
+ 'SELECT' => 'Predis\Command\ConnectionSelect',
108
+ 'ECHO' => 'Predis\Command\ConnectionEcho',
109
+ 'QUIT' => 'Predis\Command\ConnectionQuit',
110
+
111
+ /* remote server control commands */
112
+ 'INFO' => 'Predis\Command\ServerInfo',
113
+ 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
114
+ 'MONITOR' => 'Predis\Command\ServerMonitor',
115
+ 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
116
+ 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
117
+ 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
118
+ 'SAVE' => 'Predis\Command\ServerSave',
119
+ 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
120
+ 'LASTSAVE' => 'Predis\Command\ServerLastSave',
121
+ 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
122
+ 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
123
+
124
+ /* ---------------- Redis 2.0 ---------------- */
125
+
126
+ /* commands operating on string values */
127
+ 'SETEX' => 'Predis\Command\StringSetExpire',
128
+ 'APPEND' => 'Predis\Command\StringAppend',
129
+ 'SUBSTR' => 'Predis\Command\StringSubstr',
130
+
131
+ /* commands operating on lists */
132
+ 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
133
+ 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
134
+
135
+ /* commands operating on sorted sets */
136
+ 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
137
+ 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
138
+ 'ZCOUNT' => 'Predis\Command\ZSetCount',
139
+ 'ZRANK' => 'Predis\Command\ZSetRank',
140
+ 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
141
+ 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
142
+
143
+ /* commands operating on hashes */
144
+ 'HSET' => 'Predis\Command\HashSet',
145
+ 'HSETNX' => 'Predis\Command\HashSetPreserve',
146
+ 'HMSET' => 'Predis\Command\HashSetMultiple',
147
+ 'HINCRBY' => 'Predis\Command\HashIncrementBy',
148
+ 'HGET' => 'Predis\Command\HashGet',
149
+ 'HMGET' => 'Predis\Command\HashGetMultiple',
150
+ 'HDEL' => 'Predis\Command\HashDelete',
151
+ 'HEXISTS' => 'Predis\Command\HashExists',
152
+ 'HLEN' => 'Predis\Command\HashLength',
153
+ 'HKEYS' => 'Predis\Command\HashKeys',
154
+ 'HVALS' => 'Predis\Command\HashValues',
155
+ 'HGETALL' => 'Predis\Command\HashGetAll',
156
+
157
+ /* transactions */
158
+ 'MULTI' => 'Predis\Command\TransactionMulti',
159
+ 'EXEC' => 'Predis\Command\TransactionExec',
160
+ 'DISCARD' => 'Predis\Command\TransactionDiscard',
161
+
162
+ /* publish - subscribe */
163
+ 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
164
+ 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
165
+ 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
166
+ 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
167
+ 'PUBLISH' => 'Predis\Command\PubSubPublish',
168
+
169
+ /* remote server control commands */
170
+ 'CONFIG' => 'Predis\Command\ServerConfig',
171
+
172
+ /* ---------------- Redis 2.2 ---------------- */
173
+
174
+ /* commands operating on the key space */
175
+ 'PERSIST' => 'Predis\Command\KeyPersist',
176
+
177
+ /* commands operating on string values */
178
+ 'STRLEN' => 'Predis\Command\StringStrlen',
179
+ 'SETRANGE' => 'Predis\Command\StringSetRange',
180
+ 'GETRANGE' => 'Predis\Command\StringGetRange',
181
+ 'SETBIT' => 'Predis\Command\StringSetBit',
182
+ 'GETBIT' => 'Predis\Command\StringGetBit',
183
+
184
+ /* commands operating on lists */
185
+ 'RPUSHX' => 'Predis\Command\ListPushTailX',
186
+ 'LPUSHX' => 'Predis\Command\ListPushHeadX',
187
+ 'LINSERT' => 'Predis\Command\ListInsert',
188
+ 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
189
+
190
+ /* commands operating on sorted sets */
191
+ 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
192
+
193
+ /* transactions */
194
+ 'WATCH' => 'Predis\Command\TransactionWatch',
195
+ 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
196
+
197
+ /* remote server control commands */
198
+ 'OBJECT' => 'Predis\Command\ServerObject',
199
+ 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
200
+
201
+ /* ---------------- Redis 2.4 ---------------- */
202
+
203
+ /* remote server control commands */
204
+ 'CLIENT' => 'Predis\Command\ServerClient',
205
+ );
206
+ }
207
+ }
includes/predis/src/Profile/RedisVersion260.php ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for Redis 2.6.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisVersion260 extends RedisProfile
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '2.6';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+
37
+ /* commands operating on the key space */
38
+ 'EXISTS' => 'Predis\Command\KeyExists',
39
+ 'DEL' => 'Predis\Command\KeyDelete',
40
+ 'TYPE' => 'Predis\Command\KeyType',
41
+ 'KEYS' => 'Predis\Command\KeyKeys',
42
+ 'RANDOMKEY' => 'Predis\Command\KeyRandom',
43
+ 'RENAME' => 'Predis\Command\KeyRename',
44
+ 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
45
+ 'EXPIRE' => 'Predis\Command\KeyExpire',
46
+ 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
47
+ 'TTL' => 'Predis\Command\KeyTimeToLive',
48
+ 'MOVE' => 'Predis\Command\KeyMove',
49
+ 'SORT' => 'Predis\Command\KeySort',
50
+ 'DUMP' => 'Predis\Command\KeyDump',
51
+ 'RESTORE' => 'Predis\Command\KeyRestore',
52
+
53
+ /* commands operating on string values */
54
+ 'SET' => 'Predis\Command\StringSet',
55
+ 'SETNX' => 'Predis\Command\StringSetPreserve',
56
+ 'MSET' => 'Predis\Command\StringSetMultiple',
57
+ 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
58
+ 'GET' => 'Predis\Command\StringGet',
59
+ 'MGET' => 'Predis\Command\StringGetMultiple',
60
+ 'GETSET' => 'Predis\Command\StringGetSet',
61
+ 'INCR' => 'Predis\Command\StringIncrement',
62
+ 'INCRBY' => 'Predis\Command\StringIncrementBy',
63
+ 'DECR' => 'Predis\Command\StringDecrement',
64
+ 'DECRBY' => 'Predis\Command\StringDecrementBy',
65
+
66
+ /* commands operating on lists */
67
+ 'RPUSH' => 'Predis\Command\ListPushTail',
68
+ 'LPUSH' => 'Predis\Command\ListPushHead',
69
+ 'LLEN' => 'Predis\Command\ListLength',
70
+ 'LRANGE' => 'Predis\Command\ListRange',
71
+ 'LTRIM' => 'Predis\Command\ListTrim',
72
+ 'LINDEX' => 'Predis\Command\ListIndex',
73
+ 'LSET' => 'Predis\Command\ListSet',
74
+ 'LREM' => 'Predis\Command\ListRemove',
75
+ 'LPOP' => 'Predis\Command\ListPopFirst',
76
+ 'RPOP' => 'Predis\Command\ListPopLast',
77
+ 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
78
+
79
+ /* commands operating on sets */
80
+ 'SADD' => 'Predis\Command\SetAdd',
81
+ 'SREM' => 'Predis\Command\SetRemove',
82
+ 'SPOP' => 'Predis\Command\SetPop',
83
+ 'SMOVE' => 'Predis\Command\SetMove',
84
+ 'SCARD' => 'Predis\Command\SetCardinality',
85
+ 'SISMEMBER' => 'Predis\Command\SetIsMember',
86
+ 'SINTER' => 'Predis\Command\SetIntersection',
87
+ 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
88
+ 'SUNION' => 'Predis\Command\SetUnion',
89
+ 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
90
+ 'SDIFF' => 'Predis\Command\SetDifference',
91
+ 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
92
+ 'SMEMBERS' => 'Predis\Command\SetMembers',
93
+ 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
94
+
95
+ /* commands operating on sorted sets */
96
+ 'ZADD' => 'Predis\Command\ZSetAdd',
97
+ 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
98
+ 'ZREM' => 'Predis\Command\ZSetRemove',
99
+ 'ZRANGE' => 'Predis\Command\ZSetRange',
100
+ 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
101
+ 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
102
+ 'ZCARD' => 'Predis\Command\ZSetCardinality',
103
+ 'ZSCORE' => 'Predis\Command\ZSetScore',
104
+ 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
105
+
106
+ /* connection related commands */
107
+ 'PING' => 'Predis\Command\ConnectionPing',
108
+ 'AUTH' => 'Predis\Command\ConnectionAuth',
109
+ 'SELECT' => 'Predis\Command\ConnectionSelect',
110
+ 'ECHO' => 'Predis\Command\ConnectionEcho',
111
+ 'QUIT' => 'Predis\Command\ConnectionQuit',
112
+
113
+ /* remote server control commands */
114
+ 'INFO' => 'Predis\Command\ServerInfoV26x',
115
+ 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
116
+ 'MONITOR' => 'Predis\Command\ServerMonitor',
117
+ 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
118
+ 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
119
+ 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
120
+ 'SAVE' => 'Predis\Command\ServerSave',
121
+ 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
122
+ 'LASTSAVE' => 'Predis\Command\ServerLastSave',
123
+ 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
124
+ 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
125
+
126
+ /* ---------------- Redis 2.0 ---------------- */
127
+
128
+ /* commands operating on string values */
129
+ 'SETEX' => 'Predis\Command\StringSetExpire',
130
+ 'APPEND' => 'Predis\Command\StringAppend',
131
+ 'SUBSTR' => 'Predis\Command\StringSubstr',
132
+
133
+ /* commands operating on lists */
134
+ 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
135
+ 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
136
+
137
+ /* commands operating on sorted sets */
138
+ 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
139
+ 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
140
+ 'ZCOUNT' => 'Predis\Command\ZSetCount',
141
+ 'ZRANK' => 'Predis\Command\ZSetRank',
142
+ 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
143
+ 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
144
+
145
+ /* commands operating on hashes */
146
+ 'HSET' => 'Predis\Command\HashSet',
147
+ 'HSETNX' => 'Predis\Command\HashSetPreserve',
148
+ 'HMSET' => 'Predis\Command\HashSetMultiple',
149
+ 'HINCRBY' => 'Predis\Command\HashIncrementBy',
150
+ 'HGET' => 'Predis\Command\HashGet',
151
+ 'HMGET' => 'Predis\Command\HashGetMultiple',
152
+ 'HDEL' => 'Predis\Command\HashDelete',
153
+ 'HEXISTS' => 'Predis\Command\HashExists',
154
+ 'HLEN' => 'Predis\Command\HashLength',
155
+ 'HKEYS' => 'Predis\Command\HashKeys',
156
+ 'HVALS' => 'Predis\Command\HashValues',
157
+ 'HGETALL' => 'Predis\Command\HashGetAll',
158
+
159
+ /* transactions */
160
+ 'MULTI' => 'Predis\Command\TransactionMulti',
161
+ 'EXEC' => 'Predis\Command\TransactionExec',
162
+ 'DISCARD' => 'Predis\Command\TransactionDiscard',
163
+
164
+ /* publish - subscribe */
165
+ 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
166
+ 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
167
+ 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
168
+ 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
169
+ 'PUBLISH' => 'Predis\Command\PubSubPublish',
170
+
171
+ /* remote server control commands */
172
+ 'CONFIG' => 'Predis\Command\ServerConfig',
173
+
174
+ /* ---------------- Redis 2.2 ---------------- */
175
+
176
+ /* commands operating on the key space */
177
+ 'PERSIST' => 'Predis\Command\KeyPersist',
178
+
179
+ /* commands operating on string values */
180
+ 'STRLEN' => 'Predis\Command\StringStrlen',
181
+ 'SETRANGE' => 'Predis\Command\StringSetRange',
182
+ 'GETRANGE' => 'Predis\Command\StringGetRange',
183
+ 'SETBIT' => 'Predis\Command\StringSetBit',
184
+ 'GETBIT' => 'Predis\Command\StringGetBit',
185
+
186
+ /* commands operating on lists */
187
+ 'RPUSHX' => 'Predis\Command\ListPushTailX',
188
+ 'LPUSHX' => 'Predis\Command\ListPushHeadX',
189
+ 'LINSERT' => 'Predis\Command\ListInsert',
190
+ 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
191
+
192
+ /* commands operating on sorted sets */
193
+ 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
194
+
195
+ /* transactions */
196
+ 'WATCH' => 'Predis\Command\TransactionWatch',
197
+ 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
198
+
199
+ /* remote server control commands */
200
+ 'OBJECT' => 'Predis\Command\ServerObject',
201
+ 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
202
+
203
+ /* ---------------- Redis 2.4 ---------------- */
204
+
205
+ /* remote server control commands */
206
+ 'CLIENT' => 'Predis\Command\ServerClient',
207
+
208
+ /* ---------------- Redis 2.6 ---------------- */
209
+
210
+ /* commands operating on the key space */
211
+ 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
212
+ 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
213
+ 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
214
+ 'MIGRATE' => 'Predis\Command\KeyMigrate',
215
+
216
+ /* commands operating on string values */
217
+ 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
218
+ 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
219
+ 'BITOP' => 'Predis\Command\StringBitOp',
220
+ 'BITCOUNT' => 'Predis\Command\StringBitCount',
221
+
222
+ /* commands operating on hashes */
223
+ 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
224
+
225
+ /* scripting */
226
+ 'EVAL' => 'Predis\Command\ServerEval',
227
+ 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
228
+ 'SCRIPT' => 'Predis\Command\ServerScript',
229
+
230
+ /* remote server control commands */
231
+ 'TIME' => 'Predis\Command\ServerTime',
232
+ 'SENTINEL' => 'Predis\Command\ServerSentinel',
233
+ );
234
+ }
235
+ }
includes/predis/src/Profile/RedisVersion280.php ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for Redis 2.8.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisVersion280 extends RedisProfile
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '2.8';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+
37
+ /* commands operating on the key space */
38
+ 'EXISTS' => 'Predis\Command\KeyExists',
39
+ 'DEL' => 'Predis\Command\KeyDelete',
40
+ 'TYPE' => 'Predis\Command\KeyType',
41
+ 'KEYS' => 'Predis\Command\KeyKeys',
42
+ 'RANDOMKEY' => 'Predis\Command\KeyRandom',
43
+ 'RENAME' => 'Predis\Command\KeyRename',
44
+ 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
45
+ 'EXPIRE' => 'Predis\Command\KeyExpire',
46
+ 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
47
+ 'TTL' => 'Predis\Command\KeyTimeToLive',
48
+ 'MOVE' => 'Predis\Command\KeyMove',
49
+ 'SORT' => 'Predis\Command\KeySort',
50
+ 'DUMP' => 'Predis\Command\KeyDump',
51
+ 'RESTORE' => 'Predis\Command\KeyRestore',
52
+
53
+ /* commands operating on string values */
54
+ 'SET' => 'Predis\Command\StringSet',
55
+ 'SETNX' => 'Predis\Command\StringSetPreserve',
56
+ 'MSET' => 'Predis\Command\StringSetMultiple',
57
+ 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
58
+ 'GET' => 'Predis\Command\StringGet',
59
+ 'MGET' => 'Predis\Command\StringGetMultiple',
60
+ 'GETSET' => 'Predis\Command\StringGetSet',
61
+ 'INCR' => 'Predis\Command\StringIncrement',
62
+ 'INCRBY' => 'Predis\Command\StringIncrementBy',
63
+ 'DECR' => 'Predis\Command\StringDecrement',
64
+ 'DECRBY' => 'Predis\Command\StringDecrementBy',
65
+
66
+ /* commands operating on lists */
67
+ 'RPUSH' => 'Predis\Command\ListPushTail',
68
+ 'LPUSH' => 'Predis\Command\ListPushHead',
69
+ 'LLEN' => 'Predis\Command\ListLength',
70
+ 'LRANGE' => 'Predis\Command\ListRange',
71
+ 'LTRIM' => 'Predis\Command\ListTrim',
72
+ 'LINDEX' => 'Predis\Command\ListIndex',
73
+ 'LSET' => 'Predis\Command\ListSet',
74
+ 'LREM' => 'Predis\Command\ListRemove',
75
+ 'LPOP' => 'Predis\Command\ListPopFirst',
76
+ 'RPOP' => 'Predis\Command\ListPopLast',
77
+ 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
78
+
79
+ /* commands operating on sets */
80
+ 'SADD' => 'Predis\Command\SetAdd',
81
+ 'SREM' => 'Predis\Command\SetRemove',
82
+ 'SPOP' => 'Predis\Command\SetPop',
83
+ 'SMOVE' => 'Predis\Command\SetMove',
84
+ 'SCARD' => 'Predis\Command\SetCardinality',
85
+ 'SISMEMBER' => 'Predis\Command\SetIsMember',
86
+ 'SINTER' => 'Predis\Command\SetIntersection',
87
+ 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
88
+ 'SUNION' => 'Predis\Command\SetUnion',
89
+ 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
90
+ 'SDIFF' => 'Predis\Command\SetDifference',
91
+ 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
92
+ 'SMEMBERS' => 'Predis\Command\SetMembers',
93
+ 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
94
+
95
+ /* commands operating on sorted sets */
96
+ 'ZADD' => 'Predis\Command\ZSetAdd',
97
+ 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
98
+ 'ZREM' => 'Predis\Command\ZSetRemove',
99
+ 'ZRANGE' => 'Predis\Command\ZSetRange',
100
+ 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
101
+ 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
102
+ 'ZCARD' => 'Predis\Command\ZSetCardinality',
103
+ 'ZSCORE' => 'Predis\Command\ZSetScore',
104
+ 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
105
+
106
+ /* connection related commands */
107
+ 'PING' => 'Predis\Command\ConnectionPing',
108
+ 'AUTH' => 'Predis\Command\ConnectionAuth',
109
+ 'SELECT' => 'Predis\Command\ConnectionSelect',
110
+ 'ECHO' => 'Predis\Command\ConnectionEcho',
111
+ 'QUIT' => 'Predis\Command\ConnectionQuit',
112
+
113
+ /* remote server control commands */
114
+ 'INFO' => 'Predis\Command\ServerInfoV26x',
115
+ 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
116
+ 'MONITOR' => 'Predis\Command\ServerMonitor',
117
+ 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
118
+ 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
119
+ 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
120
+ 'SAVE' => 'Predis\Command\ServerSave',
121
+ 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
122
+ 'LASTSAVE' => 'Predis\Command\ServerLastSave',
123
+ 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
124
+ 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
125
+
126
+ /* ---------------- Redis 2.0 ---------------- */
127
+
128
+ /* commands operating on string values */
129
+ 'SETEX' => 'Predis\Command\StringSetExpire',
130
+ 'APPEND' => 'Predis\Command\StringAppend',
131
+ 'SUBSTR' => 'Predis\Command\StringSubstr',
132
+
133
+ /* commands operating on lists */
134
+ 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
135
+ 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
136
+
137
+ /* commands operating on sorted sets */
138
+ 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
139
+ 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
140
+ 'ZCOUNT' => 'Predis\Command\ZSetCount',
141
+ 'ZRANK' => 'Predis\Command\ZSetRank',
142
+ 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
143
+ 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
144
+
145
+ /* commands operating on hashes */
146
+ 'HSET' => 'Predis\Command\HashSet',
147
+ 'HSETNX' => 'Predis\Command\HashSetPreserve',
148
+ 'HMSET' => 'Predis\Command\HashSetMultiple',
149
+ 'HINCRBY' => 'Predis\Command\HashIncrementBy',
150
+ 'HGET' => 'Predis\Command\HashGet',
151
+ 'HMGET' => 'Predis\Command\HashGetMultiple',
152
+ 'HDEL' => 'Predis\Command\HashDelete',
153
+ 'HEXISTS' => 'Predis\Command\HashExists',
154
+ 'HLEN' => 'Predis\Command\HashLength',
155
+ 'HKEYS' => 'Predis\Command\HashKeys',
156
+ 'HVALS' => 'Predis\Command\HashValues',
157
+ 'HGETALL' => 'Predis\Command\HashGetAll',
158
+
159
+ /* transactions */
160
+ 'MULTI' => 'Predis\Command\TransactionMulti',
161
+ 'EXEC' => 'Predis\Command\TransactionExec',
162
+ 'DISCARD' => 'Predis\Command\TransactionDiscard',
163
+
164
+ /* publish - subscribe */
165
+ 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
166
+ 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
167
+ 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
168
+ 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
169
+ 'PUBLISH' => 'Predis\Command\PubSubPublish',
170
+
171
+ /* remote server control commands */
172
+ 'CONFIG' => 'Predis\Command\ServerConfig',
173
+
174
+ /* ---------------- Redis 2.2 ---------------- */
175
+
176
+ /* commands operating on the key space */
177
+ 'PERSIST' => 'Predis\Command\KeyPersist',
178
+
179
+ /* commands operating on string values */
180
+ 'STRLEN' => 'Predis\Command\StringStrlen',
181
+ 'SETRANGE' => 'Predis\Command\StringSetRange',
182
+ 'GETRANGE' => 'Predis\Command\StringGetRange',
183
+ 'SETBIT' => 'Predis\Command\StringSetBit',
184
+ 'GETBIT' => 'Predis\Command\StringGetBit',
185
+
186
+ /* commands operating on lists */
187
+ 'RPUSHX' => 'Predis\Command\ListPushTailX',
188
+ 'LPUSHX' => 'Predis\Command\ListPushHeadX',
189
+ 'LINSERT' => 'Predis\Command\ListInsert',
190
+ 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
191
+
192
+ /* commands operating on sorted sets */
193
+ 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
194
+
195
+ /* transactions */
196
+ 'WATCH' => 'Predis\Command\TransactionWatch',
197
+ 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
198
+
199
+ /* remote server control commands */
200
+ 'OBJECT' => 'Predis\Command\ServerObject',
201
+ 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
202
+
203
+ /* ---------------- Redis 2.4 ---------------- */
204
+
205
+ /* remote server control commands */
206
+ 'CLIENT' => 'Predis\Command\ServerClient',
207
+
208
+ /* ---------------- Redis 2.6 ---------------- */
209
+
210
+ /* commands operating on the key space */
211
+ 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
212
+ 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
213
+ 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
214
+ 'MIGRATE' => 'Predis\Command\KeyMigrate',
215
+
216
+ /* commands operating on string values */
217
+ 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
218
+ 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
219
+ 'BITOP' => 'Predis\Command\StringBitOp',
220
+ 'BITCOUNT' => 'Predis\Command\StringBitCount',
221
+
222
+ /* commands operating on hashes */
223
+ 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
224
+
225
+ /* scripting */
226
+ 'EVAL' => 'Predis\Command\ServerEval',
227
+ 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
228
+ 'SCRIPT' => 'Predis\Command\ServerScript',
229
+
230
+ /* remote server control commands */
231
+ 'TIME' => 'Predis\Command\ServerTime',
232
+ 'SENTINEL' => 'Predis\Command\ServerSentinel',
233
+
234
+ /* ---------------- Redis 2.8 ---------------- */
235
+
236
+ /* commands operating on the key space */
237
+ 'SCAN' => 'Predis\Command\KeyScan',
238
+
239
+ /* commands operating on string values */
240
+ 'BITPOS' => 'Predis\Command\StringBitPos',
241
+
242
+ /* commands operating on sets */
243
+ 'SSCAN' => 'Predis\Command\SetScan',
244
+
245
+ /* commands operating on sorted sets */
246
+ 'ZSCAN' => 'Predis\Command\ZSetScan',
247
+ 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
248
+ 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
249
+ 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
250
+ 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',
251
+
252
+ /* commands operating on hashes */
253
+ 'HSCAN' => 'Predis\Command\HashScan',
254
+
255
+ /* publish - subscribe */
256
+ 'PUBSUB' => 'Predis\Command\PubSubPubsub',
257
+
258
+ /* commands operating on HyperLogLog */
259
+ 'PFADD' => 'Predis\Command\HyperLogLogAdd',
260
+ 'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
261
+ 'PFMERGE' => 'Predis\Command\HyperLogLogMerge',
262
+
263
+ /* remote server control commands */
264
+ 'COMMAND' => 'Predis\Command\ServerCommand',
265
+ );
266
+ }
267
+ }
includes/predis/src/Profile/RedisVersion300.php ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for Redis 3.0.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisVersion300 extends RedisProfile
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '3.0';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+
37
+ /* commands operating on the key space */
38
+ 'EXISTS' => 'Predis\Command\KeyExists',
39
+ 'DEL' => 'Predis\Command\KeyDelete',
40
+ 'TYPE' => 'Predis\Command\KeyType',
41
+ 'KEYS' => 'Predis\Command\KeyKeys',
42
+ 'RANDOMKEY' => 'Predis\Command\KeyRandom',
43
+ 'RENAME' => 'Predis\Command\KeyRename',
44
+ 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
45
+ 'EXPIRE' => 'Predis\Command\KeyExpire',
46
+ 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
47
+ 'TTL' => 'Predis\Command\KeyTimeToLive',
48
+ 'MOVE' => 'Predis\Command\KeyMove',
49
+ 'SORT' => 'Predis\Command\KeySort',
50
+ 'DUMP' => 'Predis\Command\KeyDump',
51
+ 'RESTORE' => 'Predis\Command\KeyRestore',
52
+
53
+ /* commands operating on string values */
54
+ 'SET' => 'Predis\Command\StringSet',
55
+ 'SETNX' => 'Predis\Command\StringSetPreserve',
56
+ 'MSET' => 'Predis\Command\StringSetMultiple',
57
+ 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
58
+ 'GET' => 'Predis\Command\StringGet',
59
+ 'MGET' => 'Predis\Command\StringGetMultiple',
60
+ 'GETSET' => 'Predis\Command\StringGetSet',
61
+ 'INCR' => 'Predis\Command\StringIncrement',
62
+ 'INCRBY' => 'Predis\Command\StringIncrementBy',
63
+ 'DECR' => 'Predis\Command\StringDecrement',
64
+ 'DECRBY' => 'Predis\Command\StringDecrementBy',
65
+
66
+ /* commands operating on lists */
67
+ 'RPUSH' => 'Predis\Command\ListPushTail',
68
+ 'LPUSH' => 'Predis\Command\ListPushHead',
69
+ 'LLEN' => 'Predis\Command\ListLength',
70
+ 'LRANGE' => 'Predis\Command\ListRange',
71
+ 'LTRIM' => 'Predis\Command\ListTrim',
72
+ 'LINDEX' => 'Predis\Command\ListIndex',
73
+ 'LSET' => 'Predis\Command\ListSet',
74
+ 'LREM' => 'Predis\Command\ListRemove',
75
+ 'LPOP' => 'Predis\Command\ListPopFirst',
76
+ 'RPOP' => 'Predis\Command\ListPopLast',
77
+ 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
78
+
79
+ /* commands operating on sets */
80
+ 'SADD' => 'Predis\Command\SetAdd',
81
+ 'SREM' => 'Predis\Command\SetRemove',
82
+ 'SPOP' => 'Predis\Command\SetPop',
83
+ 'SMOVE' => 'Predis\Command\SetMove',
84
+ 'SCARD' => 'Predis\Command\SetCardinality',
85
+ 'SISMEMBER' => 'Predis\Command\SetIsMember',
86
+ 'SINTER' => 'Predis\Command\SetIntersection',
87
+ 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
88
+ 'SUNION' => 'Predis\Command\SetUnion',
89
+ 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
90
+ 'SDIFF' => 'Predis\Command\SetDifference',
91
+ 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
92
+ 'SMEMBERS' => 'Predis\Command\SetMembers',
93
+ 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
94
+
95
+ /* commands operating on sorted sets */
96
+ 'ZADD' => 'Predis\Command\ZSetAdd',
97
+ 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
98
+ 'ZREM' => 'Predis\Command\ZSetRemove',
99
+ 'ZRANGE' => 'Predis\Command\ZSetRange',
100
+ 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
101
+ 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
102
+ 'ZCARD' => 'Predis\Command\ZSetCardinality',
103
+ 'ZSCORE' => 'Predis\Command\ZSetScore',
104
+ 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
105
+
106
+ /* connection related commands */
107
+ 'PING' => 'Predis\Command\ConnectionPing',
108
+ 'AUTH' => 'Predis\Command\ConnectionAuth',
109
+ 'SELECT' => 'Predis\Command\ConnectionSelect',
110
+ 'ECHO' => 'Predis\Command\ConnectionEcho',
111
+ 'QUIT' => 'Predis\Command\ConnectionQuit',
112
+
113
+ /* remote server control commands */
114
+ 'INFO' => 'Predis\Command\ServerInfoV26x',
115
+ 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
116
+ 'MONITOR' => 'Predis\Command\ServerMonitor',
117
+ 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
118
+ 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
119
+ 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
120
+ 'SAVE' => 'Predis\Command\ServerSave',
121
+ 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
122
+ 'LASTSAVE' => 'Predis\Command\ServerLastSave',
123
+ 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
124
+ 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
125
+
126
+ /* ---------------- Redis 2.0 ---------------- */
127
+
128
+ /* commands operating on string values */
129
+ 'SETEX' => 'Predis\Command\StringSetExpire',
130
+ 'APPEND' => 'Predis\Command\StringAppend',
131
+ 'SUBSTR' => 'Predis\Command\StringSubstr',
132
+
133
+ /* commands operating on lists */
134
+ 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
135
+ 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
136
+
137
+ /* commands operating on sorted sets */
138
+ 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
139
+ 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
140
+ 'ZCOUNT' => 'Predis\Command\ZSetCount',
141
+ 'ZRANK' => 'Predis\Command\ZSetRank',
142
+ 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
143
+ 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
144
+
145
+ /* commands operating on hashes */
146
+ 'HSET' => 'Predis\Command\HashSet',
147
+ 'HSETNX' => 'Predis\Command\HashSetPreserve',
148
+ 'HMSET' => 'Predis\Command\HashSetMultiple',
149
+ 'HINCRBY' => 'Predis\Command\HashIncrementBy',
150
+ 'HGET' => 'Predis\Command\HashGet',
151
+ 'HMGET' => 'Predis\Command\HashGetMultiple',
152
+ 'HDEL' => 'Predis\Command\HashDelete',
153
+ 'HEXISTS' => 'Predis\Command\HashExists',
154
+ 'HLEN' => 'Predis\Command\HashLength',
155
+ 'HKEYS' => 'Predis\Command\HashKeys',
156
+ 'HVALS' => 'Predis\Command\HashValues',
157
+ 'HGETALL' => 'Predis\Command\HashGetAll',
158
+
159
+ /* transactions */
160
+ 'MULTI' => 'Predis\Command\TransactionMulti',
161
+ 'EXEC' => 'Predis\Command\TransactionExec',
162
+ 'DISCARD' => 'Predis\Command\TransactionDiscard',
163
+
164
+ /* publish - subscribe */
165
+ 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
166
+ 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
167
+ 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
168
+ 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
169
+ 'PUBLISH' => 'Predis\Command\PubSubPublish',
170
+
171
+ /* remote server control commands */
172
+ 'CONFIG' => 'Predis\Command\ServerConfig',
173
+
174
+ /* ---------------- Redis 2.2 ---------------- */
175
+
176
+ /* commands operating on the key space */
177
+ 'PERSIST' => 'Predis\Command\KeyPersist',
178
+
179
+ /* commands operating on string values */
180
+ 'STRLEN' => 'Predis\Command\StringStrlen',
181
+ 'SETRANGE' => 'Predis\Command\StringSetRange',
182
+ 'GETRANGE' => 'Predis\Command\StringGetRange',
183
+ 'SETBIT' => 'Predis\Command\StringSetBit',
184
+ 'GETBIT' => 'Predis\Command\StringGetBit',
185
+
186
+ /* commands operating on lists */
187
+ 'RPUSHX' => 'Predis\Command\ListPushTailX',
188
+ 'LPUSHX' => 'Predis\Command\ListPushHeadX',
189
+ 'LINSERT' => 'Predis\Command\ListInsert',
190
+ 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
191
+
192
+ /* commands operating on sorted sets */
193
+ 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
194
+
195
+ /* transactions */
196
+ 'WATCH' => 'Predis\Command\TransactionWatch',
197
+ 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
198
+
199
+ /* remote server control commands */
200
+ 'OBJECT' => 'Predis\Command\ServerObject',
201
+ 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
202
+
203
+ /* ---------------- Redis 2.4 ---------------- */
204
+
205
+ /* remote server control commands */
206
+ 'CLIENT' => 'Predis\Command\ServerClient',
207
+
208
+ /* ---------------- Redis 2.6 ---------------- */
209
+
210
+ /* commands operating on the key space */
211
+ 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
212
+ 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
213
+ 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
214
+ 'MIGRATE' => 'Predis\Command\KeyMigrate',
215
+
216
+ /* commands operating on string values */
217
+ 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
218
+ 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
219
+ 'BITOP' => 'Predis\Command\StringBitOp',
220
+ 'BITCOUNT' => 'Predis\Command\StringBitCount',
221
+
222
+ /* commands operating on hashes */
223
+ 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
224
+
225
+ /* scripting */
226
+ 'EVAL' => 'Predis\Command\ServerEval',
227
+ 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
228
+ 'SCRIPT' => 'Predis\Command\ServerScript',
229
+
230
+ /* remote server control commands */
231
+ 'TIME' => 'Predis\Command\ServerTime',
232
+ 'SENTINEL' => 'Predis\Command\ServerSentinel',
233
+
234
+ /* ---------------- Redis 2.8 ---------------- */
235
+
236
+ /* commands operating on the key space */
237
+ 'SCAN' => 'Predis\Command\KeyScan',
238
+
239
+ /* commands operating on string values */
240
+ 'BITPOS' => 'Predis\Command\StringBitPos',
241
+
242
+ /* commands operating on sets */
243
+ 'SSCAN' => 'Predis\Command\SetScan',
244
+
245
+ /* commands operating on sorted sets */
246
+ 'ZSCAN' => 'Predis\Command\ZSetScan',
247
+ 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
248
+ 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
249
+ 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
250
+ 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',
251
+
252
+ /* commands operating on hashes */
253
+ 'HSCAN' => 'Predis\Command\HashScan',
254
+
255
+ /* publish - subscribe */
256
+ 'PUBSUB' => 'Predis\Command\PubSubPubsub',
257
+
258
+ /* commands operating on HyperLogLog */
259
+ 'PFADD' => 'Predis\Command\HyperLogLogAdd',
260
+ 'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
261
+ 'PFMERGE' => 'Predis\Command\HyperLogLogMerge',
262
+
263
+ /* remote server control commands */
264
+ 'COMMAND' => 'Predis\Command\ServerCommand',
265
+
266
+ /* ---------------- Redis 3.0 ---------------- */
267
+
268
+ );
269
+ }
270
+ }
includes/predis/src/Profile/RedisVersion320.php ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Profile;
13
+
14
+ /**
15
+ * Server profile for Redis 3.0.
16
+ *
17
+ * @author Daniele Alessandri <suppakilla@gmail.com>
18
+ */
19
+ class RedisVersion320 extends RedisProfile
20
+ {
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function getVersion()
25
+ {
26
+ return '3.2';
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function getSupportedCommands()
33
+ {
34
+ return array(
35
+ /* ---------------- Redis 1.2 ---------------- */
36
+
37
+ /* commands operating on the key space */
38
+ 'EXISTS' => 'Predis\Command\KeyExists',
39
+ 'DEL' => 'Predis\Command\KeyDelete',
40
+ 'TYPE' => 'Predis\Command\KeyType',
41
+ 'KEYS' => 'Predis\Command\KeyKeys',
42
+ 'RANDOMKEY' => 'Predis\Command\KeyRandom',
43
+ 'RENAME' => 'Predis\Command\KeyRename',
44
+ 'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
45
+ 'EXPIRE' => 'Predis\Command\KeyExpire',
46
+ 'EXPIREAT' => 'Predis\Command\KeyExpireAt',
47
+ 'TTL' => 'Predis\Command\KeyTimeToLive',
48
+ 'MOVE' => 'Predis\Command\KeyMove',
49
+ 'SORT' => 'Predis\Command\KeySort',
50
+ 'DUMP' => 'Predis\Command\KeyDump',
51
+ 'RESTORE' => 'Predis\Command\KeyRestore',
52
+
53
+ /* commands operating on string values */
54
+ 'SET' => 'Predis\Command\StringSet',
55
+ 'SETNX' => 'Predis\Command\StringSetPreserve',
56
+ 'MSET' => 'Predis\Command\StringSetMultiple',
57
+ 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
58
+ 'GET' => 'Predis\Command\StringGet',
59
+ 'MGET' => 'Predis\Command\StringGetMultiple',
60
+ 'GETSET' => 'Predis\Command\StringGetSet',
61
+ 'INCR' => 'Predis\Command\StringIncrement',
62
+ 'INCRBY' => 'Predis\Command\StringIncrementBy',
63
+ 'DECR' => 'Predis\Command\StringDecrement',
64
+ 'DECRBY' => 'Predis\Command\StringDecrementBy',
65
+
66
+ /* commands operating on lists */
67
+ 'RPUSH' => 'Predis\Command\ListPushTail',
68
+ 'LPUSH' => 'Predis\Command\ListPushHead',
69
+ 'LLEN' => 'Predis\Command\ListLength',
70
+ 'LRANGE' => 'Predis\Command\ListRange',
71
+ 'LTRIM' => 'Predis\Command\ListTrim',
72
+ 'LINDEX' => 'Predis\Command\ListIndex',
73
+ 'LSET' => 'Predis\Command\ListSet',
74
+ 'LREM' => 'Predis\Command\ListRemove',
75
+ 'LPOP' => 'Predis\Command\ListPopFirst',
76
+ 'RPOP' => 'Predis\Command\ListPopLast',
77
+ 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',
78
+
79
+ /* commands operating on sets */
80
+ 'SADD' => 'Predis\Command\SetAdd',
81
+ 'SREM' => 'Predis\Command\SetRemove',
82
+ 'SPOP' => 'Predis\Command\SetPop',
83
+ 'SMOVE' => 'Predis\Command\SetMove',
84
+ 'SCARD' => 'Predis\Command\SetCardinality',
85
+ 'SISMEMBER' => 'Predis\Command\SetIsMember',
86
+ 'SINTER' => 'Predis\Command\SetIntersection',
87
+ 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
88
+ 'SUNION' => 'Predis\Command\SetUnion',
89
+ 'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
90
+ 'SDIFF' => 'Predis\Command\SetDifference',
91
+ 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
92
+ 'SMEMBERS' => 'Predis\Command\SetMembers',
93
+ 'SRANDMEMBER' => 'Predis\Command\SetRandomMember',
94
+
95
+ /* commands operating on sorted sets */
96
+ 'ZADD' => 'Predis\Command\ZSetAdd',
97
+ 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
98
+ 'ZREM' => 'Predis\Command\ZSetRemove',
99
+ 'ZRANGE' => 'Predis\Command\ZSetRange',
100
+ 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
101
+ 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
102
+ 'ZCARD' => 'Predis\Command\ZSetCardinality',
103
+ 'ZSCORE' => 'Predis\Command\ZSetScore',
104
+ 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',
105
+
106
+ /* connection related commands */
107
+ 'PING' => 'Predis\Command\ConnectionPing',
108
+ 'AUTH' => 'Predis\Command\ConnectionAuth',
109
+ 'SELECT' => 'Predis\Command\ConnectionSelect',
110
+ 'ECHO' => 'Predis\Command\ConnectionEcho',
111
+ 'QUIT' => 'Predis\Command\ConnectionQuit',
112
+
113
+ /* remote server control commands */
114
+ 'INFO' => 'Predis\Command\ServerInfoV26x',
115
+ 'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
116
+ 'MONITOR' => 'Predis\Command\ServerMonitor',
117
+ 'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
118
+ 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
119
+ 'FLUSHALL' => 'Predis\Command\ServerFlushAll',
120
+ 'SAVE' => 'Predis\Command\ServerSave',
121
+ 'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
122
+ 'LASTSAVE' => 'Predis\Command\ServerLastSave',
123
+ 'SHUTDOWN' => 'Predis\Command\ServerShutdown',
124
+ 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',
125
+
126
+ /* ---------------- Redis 2.0 ---------------- */
127
+
128
+ /* commands operating on string values */
129
+ 'SETEX' => 'Predis\Command\StringSetExpire',
130
+ 'APPEND' => 'Predis\Command\StringAppend',
131
+ 'SUBSTR' => 'Predis\Command\StringSubstr',
132
+
133
+ /* commands operating on lists */
134
+ 'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
135
+ 'BRPOP' => 'Predis\Command\ListPopLastBlocking',
136
+
137
+ /* commands operating on sorted sets */
138
+ 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
139
+ 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
140
+ 'ZCOUNT' => 'Predis\Command\ZSetCount',
141
+ 'ZRANK' => 'Predis\Command\ZSetRank',
142
+ 'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
143
+ 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',
144
+
145
+ /* commands operating on hashes */
146
+ 'HSET' => 'Predis\Command\HashSet',
147
+ 'HSETNX' => 'Predis\Command\HashSetPreserve',
148
+ 'HMSET' => 'Predis\Command\HashSetMultiple',
149
+ 'HINCRBY' => 'Predis\Command\HashIncrementBy',
150
+ 'HGET' => 'Predis\Command\HashGet',
151
+ 'HMGET' => 'Predis\Command\HashGetMultiple',
152
+ 'HDEL' => 'Predis\Command\HashDelete',
153
+ 'HEXISTS' => 'Predis\Command\HashExists',
154
+ 'HLEN' => 'Predis\Command\HashLength',
155
+ 'HKEYS' => 'Predis\Command\HashKeys',
156
+ 'HVALS' => 'Predis\Command\HashValues',
157
+ 'HGETALL' => 'Predis\Command\HashGetAll',
158
+
159
+ /* transactions */
160
+ 'MULTI' => 'Predis\Command\TransactionMulti',
161
+ 'EXEC' => 'Predis\Command\TransactionExec',
162
+ 'DISCARD' => 'Predis\Command\TransactionDiscard',
163
+
164
+ /* publish - subscribe */
165
+ 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
166
+ 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
167
+ 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
168
+ 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
169
+ 'PUBLISH' => 'Predis\Command\PubSubPublish',
170
+
171
+ /* remote server control commands */
172
+ 'CONFIG' => 'Predis\Command\ServerConfig',
173
+
174
+ /* ---------------- Redis 2.2 ---------------- */
175
+
176
+ /* commands operating on the key space */
177
+ 'PERSIST' => 'Predis\Command\KeyPersist',
178
+
179
+ /* commands operating on string values */
180
+ 'STRLEN' => 'Predis\Command\StringStrlen',
181
+ 'SETRANGE' => 'Predis\Command\StringSetRange',
182
+ 'GETRANGE' => 'Predis\Command\StringGetRange',
183
+ 'SETBIT' => 'Predis\Command\StringSetBit',
184
+ 'GETBIT' => 'Predis\Command\StringGetBit',
185
+
186
+ /* commands operating on lists */
187
+ 'RPUSHX' => 'Predis\Command\ListPushTailX',
188
+ 'LPUSHX' => 'Predis\Command\ListPushHeadX',
189
+ 'LINSERT' => 'Predis\Command\ListInsert',
190
+ 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',
191
+
192
+ /* commands operating on sorted sets */
193
+ 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',
194
+
195
+ /* transactions */
196
+ 'WATCH' => 'Predis\Command\TransactionWatch',
197
+ 'UNWATCH' => 'Predis\Command\TransactionUnwatch',
198
+
199
+ /* remote server control commands */
200
+ 'OBJECT' => 'Predis\Command\ServerObject',
201
+ 'SLOWLOG' => 'Predis\Command\ServerSlowlog',
202
+
203
+ /* ---------------- Redis 2.4 ---------------- */
204
+
205
+ /* remote server control commands */
206
+ 'CLIENT' => 'Predis\Command\ServerClient',
207
+
208
+ /* ---------------- Redis 2.6 ---------------- */
209
+
210
+ /* commands operating on the key space */
211
+ 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
212
+ 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
213
+ 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
214
+ 'MIGRATE' => 'Predis\Command\KeyMigrate',
215
+
216
+ /* commands operating on string values */
217
+ 'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
218
+ 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
219
+ 'BITOP' => 'Predis\Command\StringBitOp',
220
+ 'BITCOUNT' => 'Predis\Command\StringBitCount',
221
+
222
+ /* commands operating on hashes */
223
+ 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',
224
+
225
+ /* scripting */
226
+ 'EVAL' => 'Predis\Command\ServerEval',
227
+ 'EVALSHA' => 'Predis\Command\ServerEvalSHA',
228
+ 'SCRIPT' => 'Predis\Command\ServerScript',
229
+
230
+ /* remote server control commands */
231
+ 'TIME' => 'Predis\Command\ServerTime',
232
+ 'SENTINEL' => 'Predis\Command\ServerSentinel',
233
+
234
+ /* ---------------- Redis 2.8 ---------------- */
235
+
236
+ /* commands operating on the key space */
237
+ 'SCAN' => 'Predis\Command\KeyScan',
238
+
239
+ /* commands operating on string values */
240
+ 'BITPOS' => 'Predis\Command\StringBitPos',
241
+
242
+ /* commands operating on sets */
243
+ 'SSCAN' => 'Predis\Command\SetScan',
244
+
245
+ /* commands operating on sorted sets */
246
+ 'ZSCAN' => 'Predis\Command\ZSetScan',
247
+ 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
248
+ 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
249
+ 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
250
+ 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',
251
+
252
+ /* commands operating on hashes */
253
+ 'HSCAN' => 'Predis\Command\HashScan',
254
+
255
+ /* publish - subscribe */
256
+ 'PUBSUB' => 'Predis\Command\PubSubPubsub',
257
+
258
+ /* commands operating on HyperLogLog */
259
+ 'PFADD' => 'Predis\Command\HyperLogLogAdd',
260
+ 'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
261
+ 'PFMERGE' => 'Predis\Command\HyperLogLogMerge',
262
+
263
+ /* remote server control commands */
264
+ 'COMMAND' => 'Predis\Command\ServerCommand',
265
+
266
+ /* ---------------- Redis 3.2 ---------------- */
267
+
268
+ /* commands operating on hashes */
269
+ 'HSTRLEN' => 'Predis\Command\HashStringLength',
270
+ 'BITFIELD' => 'Predis\Command\StringBitField',
271
+
272
+ /* commands performing geospatial operations */
273
+ 'GEOADD' => 'Predis\Command\GeospatialGeoAdd',
274
+ 'GEOHASH' => 'Predis\Command\GeospatialGeoHash',
275
+ 'GEOPOS' => 'Predis\Command\GeospatialGeoPos',
276
+ 'GEODIST' => 'Predis\Command\GeospatialGeoDist',
277
+ 'GEORADIUS' => 'Predis\Command\GeospatialGeoRadius',
278
+ 'GEORADIUSBYMEMBER' => 'Predis\Command\GeospatialGeoRadiusByMember',
279
+ );
280
+ }
281
+ }
includes/predis/src/Protocol/ProtocolException.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol;
13
+
14
+ use Predis\CommunicationException;
15
+
16
+ /**
17
+ * Exception used to indentify errors encountered while parsing the Redis wire
18
+ * protocol.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ class ProtocolException extends CommunicationException
23
+ {
24
+ }
includes/predis/src/Protocol/ProtocolProcessorInterface.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Connection\CompositeConnectionInterface;
16
+
17
+ /**
18
+ * Defines a pluggable protocol processor capable of serializing commands and
19
+ * deserializing responses into PHP objects directly from a connection.
20
+ *
21
+ * @author Daniele Alessandri <suppakilla@gmail.com>
22
+ */
23
+ interface ProtocolProcessorInterface
24
+ {
25
+ /**
26
+ * Writes a request over a connection to Redis.
27
+ *
28
+ * @param CompositeConnectionInterface $connection Redis connection.
29
+ * @param CommandInterface $command Command instance.
30
+ */
31
+ public function write(CompositeConnectionInterface $connection, CommandInterface $command);
32
+
33
+ /**
34
+ * Reads a response from a connection to Redis.
35
+ *
36
+ * @param CompositeConnectionInterface $connection Redis connection.
37
+ *
38
+ * @return mixed
39
+ */
40
+ public function read(CompositeConnectionInterface $connection);
41
+ }
includes/predis/src/Protocol/RequestSerializerInterface.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol;
13
+
14
+ use Predis\Command\CommandInterface;
15
+
16
+ /**
17
+ * Defines a pluggable serializer for Redis commands.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ interface RequestSerializerInterface
22
+ {
23
+ /**
24
+ * Serializes a Redis command.
25
+ *
26
+ * @param CommandInterface $command Redis command.
27
+ *
28
+ * @return string
29
+ */
30
+ public function serialize(CommandInterface $command);
31
+ }
includes/predis/src/Protocol/ResponseReaderInterface.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol;
13
+
14
+ use Predis\Connection\CompositeConnectionInterface;
15
+
16
+ /**
17
+ * Defines a pluggable reader capable of parsing responses returned by Redis and
18
+ * deserializing them to PHP objects.
19
+ *
20
+ * @author Daniele Alessandri <suppakilla@gmail.com>
21
+ */
22
+ interface ResponseReaderInterface
23
+ {
24
+ /**
25
+ * Reads a response from a connection to Redis.
26
+ *
27
+ * @param CompositeConnectionInterface $connection Redis connection.
28
+ *
29
+ * @return mixed
30
+ */
31
+ public function read(CompositeConnectionInterface $connection);
32
+ }
includes/predis/src/Protocol/Text/CompositeProtocolProcessor.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Connection\CompositeConnectionInterface;
16
+ use Predis\Protocol\ProtocolProcessorInterface;
17
+ use Predis\Protocol\RequestSerializerInterface;
18
+ use Predis\Protocol\ResponseReaderInterface;
19
+
20
+ /**
21
+ * Composite protocol processor for the standard Redis wire protocol using
22
+ * pluggable handlers to serialize requests and deserialize responses.
23
+ *
24
+ * @link http://redis.io/topics/protocol
25
+ *
26
+ * @author Daniele Alessandri <suppakilla@gmail.com>
27
+ */
28
+ class CompositeProtocolProcessor implements ProtocolProcessorInterface
29
+ {
30
+ /*
31
+ * @var RequestSerializerInterface
32
+ */
33
+ protected $serializer;
34
+
35
+ /*
36
+ * @var ResponseReaderInterface
37
+ */
38
+ protected $reader;
39
+
40
+ /**
41
+ * @param RequestSerializerInterface $serializer Request serializer.
42
+ * @param ResponseReaderInterface $reader Response reader.
43
+ */
44
+ public function __construct(
45
+ RequestSerializerInterface $serializer = null,
46
+ ResponseReaderInterface $reader = null
47
+ ) {
48
+ $this->setRequestSerializer($serializer ?: new RequestSerializer());
49
+ $this->setResponseReader($reader ?: new ResponseReader());
50
+ }
51
+
52
+ /**
53
+ * {@inheritdoc}
54
+ */
55
+ public function write(CompositeConnectionInterface $connection, CommandInterface $command)
56
+ {
57
+ $connection->writeBuffer($this->serializer->serialize($command));
58
+ }
59
+
60
+ /**
61
+ * {@inheritdoc}
62
+ */
63
+ public function read(CompositeConnectionInterface $connection)
64
+ {
65
+ return $this->reader->read($connection);
66
+ }
67
+
68
+ /**
69
+ * Sets the request serializer used by the protocol processor.
70
+ *
71
+ * @param RequestSerializerInterface $serializer Request serializer.
72
+ */
73
+ public function setRequestSerializer(RequestSerializerInterface $serializer)
74
+ {
75
+ $this->serializer = $serializer;
76
+ }
77
+
78
+ /**
79
+ * Returns the request serializer used by the protocol processor.
80
+ *
81
+ * @return RequestSerializerInterface
82
+ */
83
+ public function getRequestSerializer()
84
+ {
85
+ return $this->serializer;
86
+ }
87
+
88
+ /**
89
+ * Sets the response reader used by the protocol processor.
90
+ *
91
+ * @param ResponseReaderInterface $reader Response reader.
92
+ */
93
+ public function setResponseReader(ResponseReaderInterface $reader)
94
+ {
95
+ $this->reader = $reader;
96
+ }
97
+
98
+ /**
99
+ * Returns the Response reader used by the protocol processor.
100
+ *
101
+ * @return ResponseReaderInterface
102
+ */
103
+ public function getResponseReader()
104
+ {
105
+ return $this->reader;
106
+ }
107
+ }
includes/predis/src/Protocol/Text/Handler/BulkResponse.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text\Handler;
13
+
14
+ use Predis\CommunicationException;
15
+ use Predis\Connection\CompositeConnectionInterface;
16
+ use Predis\Protocol\ProtocolException;
17
+
18
+ /**
19
+ * Handler for the bulk response type in the standard Redis wire protocol.
20
+ * It translates the payload to a string or a NULL.
21
+ *
22
+ * @link http://redis.io/topics/protocol
23
+ *
24
+ * @author Daniele Alessandri <suppakilla@gmail.com>
25
+ */
26
+ class BulkResponse implements ResponseHandlerInterface
27
+ {
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public function handle(CompositeConnectionInterface $connection, $payload)
32
+ {
33
+ $length = (int) $payload;
34
+
35
+ if ("$length" !== $payload) {
36
+ CommunicationException::handle(new ProtocolException(
37
+ $connection, "Cannot parse '$payload' as a valid length for a bulk response."
38
+ ));
39
+ }
40
+
41
+ if ($length >= 0) {
42
+ return substr($connection->readBuffer($length + 2), 0, -2);
43
+ }
44
+
45
+ if ($length == -1) {
46
+ return;
47
+ }
48
+
49
+ CommunicationException::handle(new ProtocolException(
50
+ $connection, "Value '$payload' is not a valid length for a bulk response."
51
+ ));
52
+
53
+ return;
54
+ }
55
+ }
includes/predis/src/Protocol/Text/Handler/ErrorResponse.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text\Handler;
13
+
14
+ use Predis\Connection\CompositeConnectionInterface;
15
+ use Predis\Response\Error;
16
+
17
+ /**
18
+ * Handler for the error response type in the standard Redis wire protocol.
19
+ * It translates the payload to a complex response object for Predis.
20
+ *
21
+ * @link http://redis.io/topics/protocol
22
+ *
23
+ * @author Daniele Alessandri <suppakilla@gmail.com>
24
+ */
25
+ class ErrorResponse implements ResponseHandlerInterface
26
+ {
27
+ /**
28
+ * {@inheritdoc}
29
+ */
30
+ public function handle(CompositeConnectionInterface $connection, $payload)
31
+ {
32
+ return new Error($payload);
33
+ }
34
+ }
includes/predis/src/Protocol/Text/Handler/IntegerResponse.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text\Handler;
13
+
14
+ use Predis\CommunicationException;
15
+ use Predis\Connection\CompositeConnectionInterface;
16
+ use Predis\Protocol\ProtocolException;
17
+
18
+ /**
19
+ * Handler for the integer response type in the standard Redis wire protocol.
20
+ * It translates the payload an integer or NULL.
21
+ *
22
+ * @link http://redis.io/topics/protocol
23
+ *
24
+ * @author Daniele Alessandri <suppakilla@gmail.com>
25
+ */
26
+ class IntegerResponse implements ResponseHandlerInterface
27
+ {
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public function handle(CompositeConnectionInterface $connection, $payload)
32
+ {
33
+ if (is_numeric($payload)) {
34
+ $integer = (int) $payload;
35
+ return $integer == $payload ? $integer : $payload;
36
+ }
37
+
38
+ if ($payload !== 'nil') {
39
+ CommunicationException::handle(new ProtocolException(
40
+ $connection, "Cannot parse '$payload' as a valid numeric response."
41
+ ));
42
+ }
43
+
44
+ return;
45
+ }
46
+ }
includes/predis/src/Protocol/Text/Handler/MultiBulkResponse.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text\Handler;
13
+
14
+ use Predis\CommunicationException;
15
+ use Predis\Connection\CompositeConnectionInterface;
16
+ use Predis\Protocol\ProtocolException;
17
+
18
+ /**
19
+ * Handler for the multibulk response type in the standard Redis wire protocol.
20
+ * It returns multibulk responses as PHP arrays.
21
+ *
22
+ * @link http://redis.io/topics/protocol
23
+ *
24
+ * @author Daniele Alessandri <suppakilla@gmail.com>
25
+ */
26
+ class MultiBulkResponse implements ResponseHandlerInterface
27
+ {
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public function handle(CompositeConnectionInterface $connection, $payload)
32
+ {
33
+ $length = (int) $payload;
34
+
35
+ if ("$length" !== $payload) {
36
+ CommunicationException::handle(new ProtocolException(
37
+ $connection, "Cannot parse '$payload' as a valid length of a multi-bulk response."
38
+ ));
39
+ }
40
+
41
+ if ($length === -1) {
42
+ return;
43
+ }
44
+
45
+ $list = array();
46
+
47
+ if ($length > 0) {
48
+ $handlersCache = array();
49
+ $reader = $connection->getProtocol()->getResponseReader();
50
+
51
+ for ($i = 0; $i < $length; ++$i) {
52
+ $header = $connection->readLine();
53
+ $prefix = $header[0];
54
+
55
+ if (isset($handlersCache[$prefix])) {
56
+ $handler = $handlersCache[$prefix];
57
+ } else {
58
+ $handler = $reader->getHandler($prefix);
59
+ $handlersCache[$prefix] = $handler;
60
+ }
61
+
62
+ $list[$i] = $handler->handle($connection, substr($header, 1));
63
+ }
64
+ }
65
+
66
+ return $list;
67
+ }
68
+ }
includes/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text\Handler;
13
+
14
+ use Predis\Connection\CompositeConnectionInterface;
15
+
16
+ /**
17
+ * Defines a pluggable handler used to parse a particular type of response.
18
+ *
19
+ * @author Daniele Alessandri <suppakilla@gmail.com>
20
+ */
21
+ interface ResponseHandlerInterface
22
+ {
23
+ /**
24
+ * Deserializes a response returned by Redis and reads more data from the
25
+ * connection if needed.
26
+ *
27
+ * @param CompositeConnectionInterface $connection Redis connection.
28
+ * @param string $payload String payload.
29
+ *
30
+ * @return mixed
31
+ */
32
+ public function handle(CompositeConnectionInterface $connection, $payload);
33
+ }
includes/predis/src/Protocol/Text/Handler/StatusResponse.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text\Handler;
13
+
14
+ use Predis\Connection\CompositeConnectionInterface;
15
+ use Predis\Response\Status;
16
+
17
+ /**
18
+ * Handler for the status response type in the standard Redis wire protocol. It
19
+ * translates certain classes of status response to PHP objects or just returns
20
+ * the payload as a string.
21
+ *
22
+ * @link http://redis.io/topics/protocol
23
+ *
24
+ * @author Daniele Alessandri <suppakilla@gmail.com>
25
+ */
26
+ class StatusResponse implements ResponseHandlerInterface
27
+ {
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public function handle(CompositeConnectionInterface $connection, $payload)
32
+ {
33
+ return Status::get($payload);
34
+ }
35
+ }
includes/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text\Handler;
13
+
14
+ use Predis\CommunicationException;
15
+ use Predis\Connection\CompositeConnectionInterface;
16
+ use Predis\Protocol\ProtocolException;
17
+ use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;
18
+
19
+ /**
20
+ * Handler for the multibulk response type in the standard Redis wire protocol.
21
+ * It returns multibulk responses as iterators that can stream bulk elements.
22
+ *
23
+ * Streamable multibulk responses are not globally supported by the abstractions
24
+ * built-in into Predis, such as transactions or pipelines. Use them with care!
25
+ *
26
+ * @link http://redis.io/topics/protocol
27
+ *
28
+ * @author Daniele Alessandri <suppakilla@gmail.com>
29
+ */
30
+ class StreamableMultiBulkResponse implements ResponseHandlerInterface
31
+ {
32
+ /**
33
+ * {@inheritdoc}
34
+ */
35
+ public function handle(CompositeConnectionInterface $connection, $payload)
36
+ {
37
+ $length = (int) $payload;
38
+
39
+ if ("$length" != $payload) {
40
+ CommunicationException::handle(new ProtocolException(
41
+ $connection, "Cannot parse '$payload' as a valid length for a multi-bulk response."
42
+ ));
43
+ }
44
+
45
+ return new MultiBulkIterator($connection, $length);
46
+ }
47
+ }
includes/predis/src/Protocol/Text/ProtocolProcessor.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\CommunicationException;
16
+ use Predis\Connection\CompositeConnectionInterface;
17
+ use Predis\Protocol\ProtocolException;
18
+ use Predis\Protocol\ProtocolProcessorInterface;
19
+ use Predis\Response\Error as ErrorResponse;
20
+ use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;
21
+ use Predis\Response\Status as StatusResponse;
22
+
23
+ /**
24
+ * Protocol processor for the standard Redis wire protocol.
25
+ *
26
+ * @link http://redis.io/topics/protocol
27
+ *
28
+ * @author Daniele Alessandri <suppakilla@gmail.com>
29
+ */
30
+ class ProtocolProcessor implements ProtocolProcessorInterface
31
+ {
32
+ protected $mbiterable;
33
+ protected $serializer;
34
+
35
+ /**
36
+ *
37
+ */
38
+ public function __construct()
39
+ {
40
+ $this->mbiterable = false;
41
+ $this->serializer = new RequestSerializer();
42
+ }
43
+
44
+ /**
45
+ * {@inheritdoc}
46
+ */
47
+ public function write(CompositeConnectionInterface $connection, CommandInterface $command)
48
+ {
49
+ $request = $this->serializer->serialize($command);
50
+ $connection->writeBuffer($request);
51
+ }
52
+
53
+ /**
54
+ * {@inheritdoc}
55
+ */
56
+ public function read(CompositeConnectionInterface $connection)
57
+ {
58
+ $chunk = $connection->readLine();
59
+ $prefix = $chunk[0];
60
+ $payload = substr($chunk, 1);
61
+
62
+ switch ($prefix) {
63
+ case '+':
64
+ return new StatusResponse($payload);
65
+
66
+ case '$':
67
+ $size = (int) $payload;
68
+ if ($size === -1) {
69
+ return;
70
+ }
71
+
72
+ return substr($connection->readBuffer($size + 2), 0, -2);
73
+
74
+ case '*':
75
+ $count = (int) $payload;
76
+
77
+ if ($count === -1) {
78
+ return;
79
+ }
80
+ if ($this->mbiterable) {
81
+ return new MultiBulkIterator($connection, $count);
82
+ }
83
+
84
+ $multibulk = array();
85
+
86
+ for ($i = 0; $i < $count; ++$i) {
87
+ $multibulk[$i] = $this->read($connection);
88
+ }
89
+
90
+ return $multibulk;
91
+
92
+ case ':':
93
+ $integer = (int) $payload;
94
+ return $integer == $payload ? $integer : $payload;
95
+
96
+ case '-':
97
+ return new ErrorResponse($payload);
98
+
99
+ default:
100
+ CommunicationException::handle(new ProtocolException(
101
+ $connection, "Unknown response prefix: '$prefix'."
102
+ ));
103
+
104
+ return;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Enables or disables returning multibulk responses as specialized PHP
110
+ * iterators used to stream bulk elements of a multibulk response instead
111
+ * returning a plain array.
112
+ *
113
+ * Streamable multibulk responses are not globally supported by the
114
+ * abstractions built-in into Predis, such as transactions or pipelines.
115
+ * Use them with care!
116
+ *
117
+ * @param bool $value Enable or disable streamable multibulk responses.
118
+ */
119
+ public function useIterableMultibulk($value)
120
+ {
121
+ $this->mbiterable = (bool) $value;
122
+ }
123
+ }
includes/predis/src/Protocol/Text/RequestSerializer.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text;
13
+
14
+ use Predis\Command\CommandInterface;
15
+ use Predis\Protocol\RequestSerializerInterface;
16
+
17
+ /**
18
+ * Request serializer for the standard Redis wire protocol.
19
+ *
20
+ * @link http://redis.io/topics/protocol
21
+ *
22
+ * @author Daniele Alessandri <suppakilla@gmail.com>
23
+ */
24
+ class RequestSerializer implements RequestSerializerInterface
25
+ {
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public function serialize(CommandInterface $command)
30
+ {
31
+ $commandID = $command->getId();
32
+ $arguments = $command->getArguments();
33
+
34
+ $cmdlen = strlen($commandID);
35
+ $reqlen = count($arguments) + 1;
36
+
37
+ $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
38
+
39
+ foreach ($arguments as $argument) {
40
+ $arglen = strlen($argument);
41
+ $buffer .= "\${$arglen}\r\n{$argument}\r\n";
42
+ }
43
+
44
+ return $buffer;
45
+ }
46
+ }
includes/predis/src/Protocol/Text/ResponseReader.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Predis package.
5
+ *
6
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Predis\Protocol\Text;
13
+
14
+ use Predis\CommunicationException;
15
+ use Predis\Connection\CompositeConnectionInterface;
16
+ use Predis\Protocol\ProtocolException;
17
+ use Predis\Protocol\ResponseReaderInterface;
18
+
19
+ /**
20
+ * Response reader for the standard Redis wire protocol.
21
+ *
22
+ * @link http://redis.io/topics/protocol
23
+ *
24
+ * @author Daniele Alessandri <suppakilla@gmail.com>
25
+ */
26
+ class ResponseReader implements ResponseReaderInterface
27
+ {
28
+ protected $handlers;
29
+
30
+ /**
31
+ *
32
+ */
33
+ public function __construct()
34
+ {
35
+ $this->handlers = $this->getDefaultHandlers();
36
+ }
37
+
38
+ /**
39
+ * Returns the default handlers for the supported type of responses.
40
+ *
41
+ * @return array
42
+ */
43
+ protected function getDefaultHandlers()
44
+ {
45
+ return array(
46
+ '+' => new Handler\StatusResponse(),
47
+ '-' => new Handler\ErrorResponse(),
48
+ ':' => new Handler\IntegerResponse(),
49
+ '$' => new Handler\BulkResponse(),
50
+ '*' => new Handler\MultiBulkResponse(),
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Sets the handler for the specified prefix identifying the response type.
56
+ *
57
+ * @param string $prefix Identifier of the type of response.
58
+ * @param Handler\ResponseHandlerInterface $handler Response handler.
59
+ */
60
+ public function setHandler($prefix, Handler\ResponseHandlerInterface $handler)
61
+ {
62
+ $this->handlers[$prefix] = $handler;
63
+ }
64
+
65
+ /**
66
+ * Returns the response handler associated to a certain type of response.
67
+ *
68
+ * @param string $prefix Identifier of the type of response.
69
+ *
70
+ * @return Handler\ResponseHandlerInterface
71
+ */
72
+ public function getHandler($prefix)
73
+ {
74
+ if (isset($this->handlers[$prefix])) {
75
+ return $this->handlers[$prefix];
76
+ }
77
+
78
+ return;
79
+ }
80
+
81
+ /**
82
+ * {@inheritdoc}
83
+ */
84
+ public function read(CompositeConnectionInterface $connection)
85
+ {
86
+ $header = $connection->readLine();
87
+
88
+ if ($header === '') {
89
+ $this->onProtocolError($connection, 'Unexpected empty reponse h