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