1818 */
1919class RedisAdapter extends AbstractAdapter
2020{
21+ private static $ defaultConnectionOptions = array (
22+ 'class ' => \Redis::class,
23+ 'persistent ' => 0 ,
24+ 'timeout ' => 0 ,
25+ 'read_timeout ' => 0 ,
26+ 'retry_interval ' => 0 ,
27+ );
2128 private $ redis ;
2229
2330 public function __construct (\Redis $ redisConnection , $ namespace = '' , $ defaultLifetime = 0 )
@@ -30,6 +37,80 @@ public function __construct(\Redis $redisConnection, $namespace = '', $defaultLi
3037 parent ::__construct ($ namespace , $ defaultLifetime );
3138 }
3239
40+ /**
41+ * Creates a Redis connection using a DSN configuration.
42+ *
43+ * Example DSN:
44+ * - redis://localhost
45+ * - redis://example.com:1234
46+ * - redis://secret@example.com/13
47+ * - redis:///var/run/redis.sock
48+ * - redis://secret@/var/run/redis.sock/13
49+ *
50+ * @param string $dsn
51+ * @param array $options See self::$defaultConnectionOptions
52+ *
53+ * @throws InvalidArgumentException When the DSN is invalid.
54+ *
55+ * @return \Redis
56+ */
57+ public static function createConnection ($ dsn , array $ options = array ())
58+ {
59+ if (0 !== strpos ($ dsn , 'redis:// ' )) {
60+ throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s does not start with "redis://" ' , $ dsn ));
61+ }
62+ $ params = preg_replace_callback ('#^redis://(?:([^@]*)@)?# ' , function ($ m ) use (&$ auth ) {
63+ if (isset ($ m [1 ])) {
64+ $ auth = $ m [1 ];
65+ }
66+
67+ return 'file:// ' ;
68+ }, $ dsn );
69+ if (false === $ params = parse_url ($ params )) {
70+ throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s ' , $ dsn ));
71+ }
72+ if (!isset ($ params ['host ' ]) && !isset ($ params ['path ' ])) {
73+ throw new InvalidArgumentException (sprintf ('Invalid Redis DSN: %s ' , $ dsn ));
74+ }
75+ if (isset ($ params ['path ' ]) && preg_match ('#/(\d+)$# ' , $ params ['path ' ], $ m )) {
76+ $ params ['dbindex ' ] = $ m [1 ];
77+ $ params ['path ' ] = substr ($ params ['path ' ], 0 , -strlen ($ m [0 ]));
78+ }
79+ $ params += array (
80+ 'host ' => isset ($ params ['host ' ]) ? $ params ['host ' ] : $ params ['path ' ],
81+ 'port ' => isset ($ params ['host ' ]) ? 6379 : null ,
82+ 'dbindex ' => 0 ,
83+ );
84+ if (isset ($ params ['query ' ])) {
85+ parse_str ($ params ['query ' ], $ query );
86+ $ params += $ query ;
87+ }
88+ $ params += $ options + self ::$ defaultConnectionOptions ;
89+
90+ if (\Redis::class !== $ params ['class ' ] && !is_subclass_of ($ params ['class ' ], \Redis::class)) {
91+ throw new InvalidArgumentException (sprintf ('"%s" is not a subclass of "Redis" ' , $ params ['class ' ]));
92+ }
93+ $ connect = empty ($ params ['persistent ' ]) ? 'connect ' : 'pconnect ' ;
94+ $ redis = new $ params ['class ' ]();
95+ @$ redis ->{$ connect }($ params ['host ' ], $ params ['port ' ], $ params ['timeout ' ], null , $ params ['retry_interval ' ]);
96+
97+ if (@!$ redis ->isConnected ()) {
98+ $ e = ($ e = error_get_last ()) && preg_match ('/^Redis::p?connect\(\): (.*)/ ' , $ e ['message ' ], $ e ) ? sprintf (' (%s) ' , $ e [1 ]) : '' ;
99+ throw new InvalidArgumentException (sprintf ('Redis connection failed%s: %s ' , $ e , $ dsn ));
100+ }
101+
102+ if ((null !== $ auth && !$ redis ->auth ($ auth ))
103+ || ($ params ['dbindex ' ] && !$ redis ->select ($ params ['dbindex ' ]))
104+ || ($ params ['read_timeout ' ] && !$ redis ->setOption (\Redis::OPT_READ_TIMEOUT , $ params ['read_timeout ' ]))
105+ ) {
106+ $ e = preg_replace ('/^ERR / ' , '' , $ redis ->getLastError ());
107+ $ redis ->close ();
108+ throw new InvalidArgumentException (sprintf ('Redis connection failed (%s): %s ' , $ e , $ dsn ));
109+ }
110+
111+ return $ redis ;
112+ }
113+
33114 /**
34115 * {@inheritdoc}
35116 */
0 commit comments