@@ -38,6 +38,7 @@ trait RedisTrait
38
38
'compression ' => true ,
39
39
'tcp_keepalive ' => 0 ,
40
40
'lazy ' => false ,
41
+ 'cluster ' => null ,
41
42
);
42
43
private $ redis ;
43
44
private $ marshaller ;
@@ -52,7 +53,7 @@ private function init($redisClient, $namespace, $defaultLifetime, ?MarshallerInt
52
53
if (preg_match ('#[^-+_.A-Za-z0-9]# ' , $ namespace , $ match )) {
53
54
throw new InvalidArgumentException (sprintf ('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed. ' , $ match [0 ]));
54
55
}
55
- if (!$ redisClient instanceof \Redis && !$ redisClient instanceof \RedisArray && !$ redisClient instanceof \RedisCluster && !$ redisClient instanceof \Predis \Client && !$ redisClient instanceof RedisProxy) {
56
+ if (!$ redisClient instanceof \Redis && !$ redisClient instanceof \RedisArray && !$ redisClient instanceof \RedisCluster && !$ redisClient instanceof \Predis \Client && !$ redisClient instanceof RedisProxy && ! $ redisClient instanceof RedisClusterProxy ) {
56
57
throw new InvalidArgumentException (sprintf ('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given ' , __METHOD__ , \is_object ($ redisClient ) ? \get_class ($ redisClient ) : \gettype ($ redisClient )));
57
58
}
58
59
$ this ->redis = $ redisClient ;
@@ -74,57 +75,59 @@ private function init($redisClient, $namespace, $defaultLifetime, ?MarshallerInt
74
75
*
75
76
* @throws InvalidArgumentException when the DSN is invalid
76
77
*
77
- * @return \Redis|\Predis\Client According to the "class" option
78
+ * @return \Redis|\RedisCluster|\ Predis\Client According to the "class" option
78
79
*/
79
80
public static function createConnection ($ dsn , array $ options = array ())
80
81
{
81
82
if (0 !== strpos ($ dsn , 'redis:// ' )) {
82
83
throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s does not start with "redis://" ' , $ dsn ));
83
84
}
84
- $ params = preg_replace_callback ('#^redis://(?:(?:[^:@]*+:)?([^@]*+)@)?# ' , function ($ m ) use (&$ auth ) {
85
- if (isset ($ m [1 ])) {
86
- $ auth = $ m [1 ];
87
- }
88
85
89
- return 'file:// ' ;
90
- }, $ dsn );
91
- if (false === $ params = parse_url ($ params )) {
86
+ if (false === $ params = parse_url ($ dsn )) {
92
87
throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s ' , $ dsn ));
93
88
}
89
+
94
90
if (!isset ($ params ['host ' ]) && !isset ($ params ['path ' ])) {
95
91
throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s ' , $ dsn ));
96
92
}
93
+
94
+ $ auth = $ params ['password ' ] ?? $ params ['user ' ] ?? null ;
95
+ $ scheme = isset ($ params ['host ' ]) ? 'tcp ' : 'unix ' ;
96
+
97
97
if (isset ($ params ['path ' ]) && preg_match ('#/(\d+)$# ' , $ params ['path ' ], $ m )) {
98
98
$ params ['dbindex ' ] = $ m [1 ];
99
99
$ params ['path ' ] = substr ($ params ['path ' ], 0 , -\strlen ($ m [0 ]));
100
100
}
101
- if (isset ($ params ['host ' ])) {
102
- $ scheme = 'tcp ' ;
103
- } else {
104
- $ scheme = 'unix ' ;
105
- }
101
+
106
102
$ params += array (
107
- 'host ' => isset ( $ params ['host ' ]) ? $ params [ ' host ' ] : $ params [ ' path ' ] ,
103
+ 'host ' => $ params ['path ' ] ?? '' ,
108
104
'port ' => isset ($ params ['host ' ]) ? 6379 : null ,
109
105
'dbindex ' => 0 ,
110
106
);
107
+
111
108
if (isset ($ params ['query ' ])) {
112
109
parse_str ($ params ['query ' ], $ query );
113
110
$ params += $ query ;
114
111
}
112
+
115
113
$ params += $ options + self ::$ defaultConnectionOptions ;
116
114
if (null === $ params ['class ' ] && !\extension_loaded ('redis ' ) && !class_exists (\Predis \Client::class)) {
117
115
throw new CacheException (sprintf ('Cannot find the "redis" extension, and "predis/predis" is not installed: %s ' , $ dsn ));
118
116
}
119
- $ class = null === $ params ['class ' ] ? (\extension_loaded ('redis ' ) ? \Redis::class : \Predis \Client::class) : $ params ['class ' ];
117
+
118
+ if (null === $ params ['class ' ] && \extension_loaded ('redis ' )) {
119
+ $ class = 'server ' === $ params ['cluster ' ] ? \RedisCluster::class : \Redis::class;
120
+ } else {
121
+ $ class = null === $ params ['class ' ] ? \Predis \Client::class : $ params ['class ' ];
122
+ }
120
123
121
124
if (is_a ($ class , \Redis::class, true )) {
122
125
$ connect = $ params ['persistent ' ] || $ params ['persistent_id ' ] ? 'pconnect ' : 'connect ' ;
123
126
$ redis = new $ class ();
124
127
125
128
$ initializer = function ($ redis ) use ($ connect , $ params , $ dsn , $ auth ) {
126
129
try {
127
- @$ redis ->{$ connect }($ params ['host ' ], $ params ['port ' ], $ params ['timeout ' ], $ params ['persistent_id ' ], $ params ['retry_interval ' ]);
130
+ @$ redis ->{$ connect }($ params ['host ' ], $ params ['port ' ], $ params ['timeout ' ], ( string ) $ params ['persistent_id ' ], $ params ['retry_interval ' ]);
128
131
} catch (\RedisException $ e ) {
129
132
throw new InvalidArgumentException (sprintf ('Redis connection failed (%s): %s ' , $ e ->getMessage (), $ dsn ));
130
133
}
@@ -160,6 +163,24 @@ public static function createConnection($dsn, array $options = array())
160
163
} else {
161
164
$ initializer ($ redis );
162
165
}
166
+ } elseif (is_a ($ class , \RedisCluster::class, true )) {
167
+ $ initializer = function () use ($ class , $ params , $ dsn , $ auth ) {
168
+ $ host = $ params ['host ' ];
169
+ if (isset ($ params ['port ' ])) {
170
+ $ host .= ': ' .$ params ['port ' ];
171
+ }
172
+
173
+ try {
174
+ /** @var \RedisCluster $redis */
175
+ $ redis = new $ class (null , explode (', ' , $ host ), $ params ['timeout ' ], $ params ['read_timeout ' ], (bool ) $ params ['persistent ' ]);
176
+ } catch (\RedisClusterException $ e ) {
177
+ throw new InvalidArgumentException (sprintf ('Redis connection failed (%s): %s ' , $ e ->getMessage (), $ dsn ));
178
+ }
179
+
180
+ return $ redis ;
181
+ };
182
+
183
+ $ redis = $ params ['lazy ' ] ? new RedisClusterProxy ($ initializer ) : $ initializer ();
163
184
} elseif (is_a ($ class , \Predis \Client::class, true )) {
164
185
$ params ['scheme ' ] = $ scheme ;
165
186
$ params ['database ' ] = $ params ['dbindex ' ] ?: null ;
@@ -183,9 +204,7 @@ protected function doFetch(array $ids)
183
204
return array ();
184
205
}
185
206
186
- $ i = -1 ;
187
207
$ result = array ();
188
-
189
208
if ($ this ->redis instanceof \Predis \Client) {
190
209
$ values = $ this ->pipeline (function () use ($ ids ) {
191
210
foreach ($ ids as $ id ) {
@@ -210,6 +229,11 @@ protected function doFetch(array $ids)
210
229
*/
211
230
protected function doHave ($ id )
212
231
{
232
+ if (!\is_string ($ id )) {
233
+ // SEGFAULT on \RedisCluster
234
+ return false ;
235
+ }
236
+
213
237
return (bool ) $ this ->redis ->exists ($ id );
214
238
}
215
239
@@ -237,13 +261,14 @@ protected function doClear($namespace)
237
261
foreach ($ this ->redis ->_hosts () as $ host ) {
238
262
$ hosts [] = $ this ->redis ->_instance ($ host );
239
263
}
240
- } elseif ($ this ->redis instanceof \RedisCluster) {
264
+ } elseif ($ this ->redis instanceof RedisClusterProxy || $ this -> redis instanceof \RedisCluster) {
241
265
$ hosts = array ();
242
266
foreach ($ this ->redis ->_masters () as $ host ) {
243
267
$ hosts [] = $ h = new \Redis ();
244
268
$ h ->connect ($ host [0 ], $ host [1 ]);
245
269
}
246
270
}
271
+
247
272
foreach ($ hosts as $ host ) {
248
273
if (!isset ($ namespace [0 ])) {
249
274
$ cleared = $ host ->flushDb () && $ cleared ;
@@ -330,7 +355,7 @@ private function pipeline(\Closure $generator)
330
355
{
331
356
$ ids = array ();
332
357
333
- if ($ this ->redis instanceof \RedisCluster || ($ this ->redis instanceof \Predis \Client && $ this ->redis ->getConnection () instanceof RedisCluster)) {
358
+ if ($ this ->redis instanceof RedisClusterProxy || $ this -> redis instanceof \RedisCluster || ($ this ->redis instanceof \Predis \Client && $ this ->redis ->getConnection () instanceof RedisCluster)) {
334
359
// phpredis & predis don't support pipelining with RedisCluster
335
360
// see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining
336
361
// see https://github.com/nrk/predis/issues/267#issuecomment-123781423
0 commit comments