55use PHPUnit \Framework \TestCase ;
66use Redis ;
77
8+ if (!class_exists (Redis::class)) {
9+ class TestRedisClientBase {}
10+ class_alias (TestRedisClientBase::class, "Redis " );
11+ }
12+
813class RedisHandlerTest extends TestCase {
914 public function testOpenParsesStandardDsn ():void {
1015 $ client = new TestRedisClient ();
@@ -91,9 +96,44 @@ protected function createClient():Redis {
9196 self ::assertTrue ($ sut ->close ());
9297 self ::assertTrue ($ client ->closed );
9398 }
99+
100+ public function testEmptyNativePhpSessionWriteDoesNotOverwriteStoredSession ():void {
101+ $ client = new TestRedisClient ();
102+ $ sut = new class ($ client ) extends RedisHandler {
103+ public function __construct (private readonly TestRedisClient $ client ) {}
104+
105+ protected function createClient ():Redis {
106+ /** @phpstan-ignore-next-line */
107+ return $ this ->client ;
108+ }
109+ };
110+ $ sut ->open ("redis://cache.internal " , "GT " );
111+ $ sut ->write ("abc123 " , "stored-session " );
112+ $ sut ->write ("abc123 " , "a:0:{} " );
113+
114+ self ::assertSame ("stored-session " , $ sut ->read ("abc123 " ));
115+ }
116+
117+ public function testCommandRetriesAfterDisconnectedClient ():void {
118+ $ client = new TestRedisClient ();
119+ $ sut = new class ($ client ) extends RedisHandler {
120+ public function __construct (private readonly TestRedisClient $ client ) {}
121+
122+ protected function createClient ():Redis {
123+ /** @phpstan-ignore-next-line */
124+ return $ this ->client ;
125+ }
126+ };
127+ $ sut ->open ("redis://cache.internal " , "GT " );
128+ $ client ->failNextSetEx = true ;
129+
130+ self ::assertTrue ($ sut ->write ("abc123 " , "payload " ));
131+ self ::assertSame ("payload " , $ sut ->read ("abc123 " ));
132+ self ::assertSame (2 , $ client ->connectCount );
133+ }
94134}
95135
96- class TestRedisClient {
136+ class TestRedisClient extends Redis {
97137 /** @var array<string,mixed> */
98138 public array $ connectParameters = [];
99139 /** @var array<int,array{key:string,ttl:int,value:string}> */
@@ -107,27 +147,30 @@ class TestRedisClient {
107147 /** @var array<string,string> */
108148 public array $ data = [];
109149 public int $ deleted = 0 ;
150+ public int $ connectCount = 0 ;
151+ public bool $ failNextSetEx = false ;
110152 public bool $ closed = false ;
111153
112154 /**
113155 * @param array{auth?:array{0:string|false|null,1?:string},stream?:array<string,mixed>}|null $context
114156 */
115157 public function connect (
116158 string $ host ,
117- int $ port ,
159+ int $ port = 6379 ,
118160 float $ timeout = 0 ,
119- ?string $ persistentId = null ,
120- int $ retryInterval = 0 ,
121- float $ readTimeout = 0 ,
161+ ?string $ persistent_id = null ,
162+ int $ retry_interval = 0 ,
163+ float $ read_timeout = 0 ,
122164 ?array $ context = null ,
123165 ):bool {
166+ $ this ->connectCount ++;
124167 $ this ->connectParameters = [
125168 "host " => $ host ,
126169 "port " => $ port ,
127170 "timeout " => $ timeout ,
128- "persistentId " => $ persistentId ,
129- "retryInterval " => $ retryInterval ,
130- "readTimeout " => $ readTimeout ,
171+ "persistentId " => $ persistent_id ,
172+ "retryInterval " => $ retry_interval ,
173+ "readTimeout " => $ read_timeout ,
131174 "context " => $ context ,
132175 ];
133176 return true ;
@@ -136,12 +179,12 @@ public function connect(
136179 /**
137180 * @param array{string,string}|string $credentials
138181 */
139- public function auth (array | string $ credentials ):bool {
182+ public function auth (mixed $ credentials ):Redis | bool {
140183 $ this ->authCalls []= $ credentials ;
141184 return true ;
142185 }
143186
144- public function select (int $ database ):bool {
187+ public function select (int $ database ):Redis | bool {
145188 $ this ->selectCalls []= $ database ;
146189 return true ;
147190 }
@@ -150,23 +193,32 @@ public function get(string $key):string|false {
150193 return $ this ->data [$ key ] ?? false ;
151194 }
152195
153- public function set (string $ key , string $ value): bool {
196+ public function set (string $ key , mixed $ value, mixed $ options = null ): Redis | string | bool {
154197 $ this ->setCalls []= $ key ;
155- $ this ->data [$ key ] = $ value ;
198+ $ this ->data [$ key ] = ( string ) $ value ;
156199 return true ;
157200 }
158201
159- public function setEx (string $ key , int $ ttl , string $ value ):bool {
202+ public function setEx (string $ key , int $ ttl , mixed $ value ) {
203+ if ($ this ->failNextSetEx ) {
204+ $ this ->failNextSetEx = false ;
205+ throw new \RedisException ("Redis server went away " );
206+ }
207+
160208 $ this ->setExCalls []= [
161209 "key " => $ key ,
162210 "ttl " => $ ttl ,
163- "value " => $ value ,
211+ "value " => ( string ) $ value ,
164212 ];
165- $ this ->data [$ key ] = $ value ;
213+ $ this ->data [$ key ] = ( string ) $ value ;
166214 return true ;
167215 }
168216
169- public function del (string $ key ):int {
217+ public function del (array |string $ key , string ...$ otherKeys ):Redis |int |false {
218+ if (is_array ($ key )) {
219+ $ key = reset ($ key );
220+ }
221+
170222 unset($ this ->data [$ key ]);
171223 return ++$ this ->deleted ;
172224 }
@@ -176,7 +228,3 @@ public function close():bool {
176228 return true ;
177229 }
178230}
179-
180- if (!class_exists (Redis::class)) {
181- class_alias (TestRedisClient::class, Redis::class);
182- }
0 commit comments