@@ -55,7 +55,7 @@ private function init($redisClient, $namespace, $defaultLifetime, ?MarshallerInt
55
55
}
56
56
if ($ redisClient instanceof \RedisCluster) {
57
57
$ this ->enableVersioning ();
58
- } elseif (!$ redisClient instanceof \Redis && !$ redisClient instanceof \RedisArray && !$ redisClient instanceof \Predis \Client && !$ redisClient instanceof RedisProxy) {
58
+ } elseif (!$ redisClient instanceof \Redis && !$ redisClient instanceof \RedisArray && !$ redisClient instanceof \Predis \Client && !$ redisClient instanceof RedisProxy && ! $ redisClient instanceof RedisClusterProxy ) {
59
59
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 )));
60
60
}
61
61
$ this ->redis = $ redisClient ;
@@ -81,53 +81,55 @@ private function init($redisClient, $namespace, $defaultLifetime, ?MarshallerInt
81
81
*/
82
82
public static function createConnection ($ dsn , array $ options = array ())
83
83
{
84
- if (0 !== strpos ($ dsn , 'redis:// ' )) {
85
- throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s does not start with "redis://" ' , $ dsn ));
84
+ if (0 !== strpos ($ dsn , 'redis:// ' ) && 0 !== strpos ( $ dsn , ' redis+cluster:// ' ) ) {
85
+ throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s does not start with "redis://" or "redis+cluster://" ' , $ dsn ));
86
86
}
87
- $ params = preg_replace_callback ('#^redis://(?:(?:[^:@]*+:)?([^@]*+)@)?# ' , function ($ m ) use (&$ auth ) {
88
- if (isset ($ m [1 ])) {
89
- $ auth = $ m [1 ];
90
- }
91
87
92
- return 'file:// ' ;
93
- }, $ dsn );
94
- if (false === $ params = parse_url ($ params )) {
88
+ if (false === $ params = parse_url ($ dsn )) {
95
89
throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s ' , $ dsn ));
96
90
}
91
+
97
92
if (!isset ($ params ['host ' ]) && !isset ($ params ['path ' ])) {
98
93
throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s ' , $ dsn ));
99
94
}
95
+
96
+ $ auth = $ params ['password ' ] ?? $ params ['user ' ] ?? null ;
97
+ $ scheme = isset ($ params ['host ' ]) ? 'tcp ' : 'unix ' ;
98
+
100
99
if (isset ($ params ['path ' ]) && preg_match ('#/(\d+)$# ' , $ params ['path ' ], $ m )) {
101
100
$ params ['dbindex ' ] = $ m [1 ];
102
101
$ params ['path ' ] = substr ($ params ['path ' ], 0 , -\strlen ($ m [0 ]));
103
102
}
104
- if (isset ($ params ['host ' ])) {
105
- $ scheme = 'tcp ' ;
106
- } else {
107
- $ scheme = 'unix ' ;
108
- }
103
+
109
104
$ params += array (
110
- 'host ' => isset ( $ params ['host ' ]) ? $ params [ ' host ' ] : $ params [ ' path ' ] ,
105
+ 'host ' => $ params ['path ' ] ?? '' ,
111
106
'port ' => isset ($ params ['host ' ]) ? 6379 : null ,
112
107
'dbindex ' => 0 ,
113
108
);
109
+
114
110
if (isset ($ params ['query ' ])) {
115
111
parse_str ($ params ['query ' ], $ query );
116
112
$ params += $ query ;
117
113
}
114
+
118
115
$ params += $ options + self ::$ defaultConnectionOptions ;
119
116
if (null === $ params ['class ' ] && !\extension_loaded ('redis ' ) && !class_exists (\Predis \Client::class)) {
120
117
throw new CacheException (sprintf ('Cannot find the "redis" extension, and "predis/predis" is not installed: %s ' , $ dsn ));
121
118
}
122
- $ class = null === $ params ['class ' ] ? (\extension_loaded ('redis ' ) ? \Redis::class : \Predis \Client::class) : $ params ['class ' ];
119
+
120
+ if (null === $ params ['class ' ] && \extension_loaded ('redis ' )) {
121
+ $ class = 'redis+cluster ' === $ params ['scheme ' ] ? \RedisCluster::class : \Redis::class;
122
+ } else {
123
+ $ class = null === $ params ['class ' ] ? \Predis \Client::class : $ params ['class ' ];
124
+ }
123
125
124
126
if (is_a ($ class , \Redis::class, true )) {
125
127
$ connect = $ params ['persistent ' ] || $ params ['persistent_id ' ] ? 'pconnect ' : 'connect ' ;
126
128
$ redis = new $ class ();
127
129
128
130
$ initializer = function ($ redis ) use ($ connect , $ params , $ dsn , $ auth ) {
129
131
try {
130
- @$ redis ->{$ connect }($ params ['host ' ], $ params ['port ' ], $ params ['timeout ' ], $ params ['persistent_id ' ], $ params ['retry_interval ' ]);
132
+ @$ redis ->{$ connect }($ params ['host ' ], $ params ['port ' ], $ params ['timeout ' ], ( string ) $ params ['persistent_id ' ], $ params ['retry_interval ' ]);
131
133
} catch (\RedisException $ e ) {
132
134
throw new InvalidArgumentException (sprintf ('Redis connection failed (%s): %s ' , $ e ->getMessage (), $ dsn ));
133
135
}
@@ -163,6 +165,24 @@ public static function createConnection($dsn, array $options = array())
163
165
} else {
164
166
$ initializer ($ redis );
165
167
}
168
+ } elseif (is_a ($ class , \RedisCluster::class, true )) {
169
+ $ initializer = function () use ($ class , $ params , $ dsn , $ auth ) {
170
+ $ host = $ params ['host ' ];
171
+ if (isset ($ params ['port ' ])) {
172
+ $ host .= ': ' .$ params ['port ' ];
173
+ }
174
+
175
+ try {
176
+ /** @var \RedisCluster $redis */
177
+ $ redis = new $ class (null , explode (', ' , $ host ), $ params ['timeout ' ], $ params ['read_timeout ' ], (bool ) $ params ['persistent ' ]);
178
+ } catch (\RedisClusterException $ e ) {
179
+ throw new InvalidArgumentException (sprintf ('Redis connection failed (%s): %s ' , $ e ->getMessage (), $ dsn ));
180
+ }
181
+
182
+ return $ redis ;
183
+ };
184
+
185
+ $ redis = $ params ['lazy ' ] ? new RedisClusterProxy ($ initializer ) : $ initializer ();
166
186
} elseif (is_a ($ class , \Predis \Client::class, true )) {
167
187
$ params ['scheme ' ] = $ scheme ;
168
188
$ params ['database ' ] = $ params ['dbindex ' ] ?: null ;
@@ -201,6 +221,11 @@ protected function doFetch(array $ids)
201
221
*/
202
222
protected function doHave ($ id )
203
223
{
224
+ if (!\is_string ($ id )) {
225
+ // SEGFAULT on \RedisCluster
226
+ return false ;
227
+ }
228
+
204
229
return (bool ) $ this ->redis ->exists ($ id );
205
230
}
206
231
@@ -233,9 +258,28 @@ protected function doClear($namespace)
233
258
foreach ($ this ->redis ->_hosts () as $ host ) {
234
259
$ hosts [] = $ this ->redis ->_instance ($ host );
235
260
}
236
- } elseif ($ this ->redis instanceof \RedisCluster) {
237
- return false ;
261
+ } elseif ($ this ->redis instanceof \RedisCluster || $ this ->redis instanceof RedisClusterProxy) {
262
+ $ keys = array ();
263
+
264
+ foreach ($ this ->redis ->_masters () as $ nodeParams ) {
265
+ $ host = new \Redis ();
266
+ $ host ->connect ($ nodeParams [0 ], $ nodeParams [1 ]);
267
+
268
+ $ cursor = null ;
269
+ do {
270
+ $ keys = array_merge ($ keys , $ host ->scan ($ cursor , $ namespace .'* ' , 1000 ));
271
+ if (isset ($ keys [1 ]) && \is_array ($ keys [1 ])) {
272
+ $ cursor = $ keys [0 ];
273
+ $ keys = $ keys [1 ];
274
+ }
275
+ } while ($ cursor = (int ) $ cursor );
276
+ }
277
+
278
+ $ this ->redis ->del ($ keys );
279
+
280
+ return true ;
238
281
}
282
+
239
283
foreach ($ hosts as $ host ) {
240
284
if (!isset ($ namespace [0 ])) {
241
285
$ cleared = $ host ->flushDb () && $ cleared ;
@@ -336,7 +380,7 @@ private function pipeline(\Closure $generator)
336
380
foreach ($ results as $ k => list ($ h , $ c )) {
337
381
$ results [$ k ] = $ connections [$ h ][$ c ];
338
382
}
339
- } elseif ($ this ->redis instanceof \RedisCluster || ($ this ->redis instanceof \Predis \Client && $ this ->redis ->getConnection () instanceof ClusterInterface)) {
383
+ } elseif ($ this ->redis instanceof RedisClusterProxy || $ this -> redis instanceof \RedisCluster || ($ this ->redis instanceof \Predis \Client && $ this ->redis ->getConnection () instanceof ClusterInterface)) {
340
384
// phpredis & predis don't support pipelining with RedisCluster
341
385
// see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining
342
386
// see https://github.com/nrk/predis/issues/267#issuecomment-123781423
0 commit comments