@@ -115,6 +115,8 @@ protected function setSavePath(): void
115115 *
116116 * @param string $path The path where to store/retrieve the session
117117 * @param string $name The session name
118+ *
119+ * @throws RedisException
118120 */
119121 public function open ($ path , $ name ): bool
120122 {
@@ -124,12 +126,20 @@ public function open($path, $name): bool
124126
125127 $ redis = new Redis ();
126128
127- if (! $ redis ->connect ($ this ->savePath ['protocol ' ] . ':// ' . $ this ->savePath ['host ' ], ($ this ->savePath ['host ' ][0 ] === '/ ' ? 0 : $ this ->savePath ['port ' ]), $ this ->savePath ['timeout ' ])) {
129+ if (
130+ ! $ redis ->connect (
131+ $ this ->savePath ['protocol ' ] . ':// ' . $ this ->savePath ['host ' ],
132+ ($ this ->savePath ['host ' ][0 ] === '/ ' ? 0 : $ this ->savePath ['port ' ]),
133+ $ this ->savePath ['timeout ' ]
134+ )
135+ ) {
128136 $ this ->logger ->error ('Session: Unable to connect to Redis with the configured settings. ' );
129137 } elseif (isset ($ this ->savePath ['password ' ]) && ! $ redis ->auth ($ this ->savePath ['password ' ])) {
130138 $ this ->logger ->error ('Session: Unable to authenticate to Redis instance. ' );
131139 } elseif (isset ($ this ->savePath ['database ' ]) && ! $ redis ->select ($ this ->savePath ['database ' ])) {
132- $ this ->logger ->error ('Session: Unable to select Redis database with index ' . $ this ->savePath ['database ' ]);
140+ $ this ->logger ->error (
141+ 'Session: Unable to select Redis database with index ' . $ this ->savePath ['database ' ]
142+ );
133143 } else {
134144 $ this ->redis = $ redis ;
135145
@@ -146,6 +156,8 @@ public function open($path, $name): bool
146156 *
147157 * @return false|string Returns an encoded string of the read data.
148158 * If nothing was read, it must return false.
159+ *
160+ * @throws RedisException
149161 */
150162 #[ReturnTypeWillChange]
151163 public function read ($ id )
@@ -168,14 +180,16 @@ public function read($id)
168180 return $ data ;
169181 }
170182
171- return '' ;
183+ return false ;
172184 }
173185
174186 /**
175187 * Writes the session data to the session storage.
176188 *
177189 * @param string $id The session ID
178190 * @param string $data The encoded session data
191+ *
192+ * @throws RedisException
179193 */
180194 public function write ($ id , $ data ): bool
181195 {
@@ -222,8 +236,8 @@ public function close(): bool
222236 $ pingReply = $ this ->redis ->ping ();
223237
224238 if (($ pingReply === true ) || ($ pingReply === '+PONG ' )) {
225- if (isset ($ this ->lockKey )) {
226- $ this -> releaseLock () ;
239+ if (isset ($ this ->lockKey ) && ! $ this -> releaseLock () ) {
240+ return false ;
227241 }
228242
229243 if (! $ this ->redis ->close ()) {
@@ -246,12 +260,16 @@ public function close(): bool
246260 * Destroys a session
247261 *
248262 * @param string $id The session ID being destroyed
263+ *
264+ * @throws RedisException
249265 */
250266 public function destroy ($ id ): bool
251267 {
252268 if (isset ($ this ->redis , $ this ->lockKey )) {
253269 if (($ result = $ this ->redis ->del ($ this ->keyPrefix . $ id )) !== 1 ) {
254- $ this ->logger ->debug ('Session: Redis::del() expected to return 1, got ' . var_export ($ result , true ) . ' instead. ' );
270+ $ this ->logger ->debug (
271+ 'Session: Redis::del() expected to return 1, got ' . var_export ($ result , true ) . ' instead. '
272+ );
255273 }
256274
257275 return $ this ->destroyCookie ();
@@ -278,6 +296,8 @@ public function gc($max_lifetime)
278296 * Acquires an emulated lock.
279297 *
280298 * @param string $sessionID Session ID
299+ *
300+ * @throws RedisException
281301 */
282302 protected function lockSession (string $ sessionID ): bool
283303 {
@@ -287,48 +307,49 @@ protected function lockSession(string $sessionID): bool
287307 // so we need to check here if the lock key is for the
288308 // correct session ID.
289309 if ($ this ->lockKey === $ lockKey ) {
310+ // If there is the lock, make the ttl longer.
290311 return $ this ->redis ->expire ($ this ->lockKey , 300 );
291312 }
292313
293314 $ attempt = 0 ;
294315
295316 do {
296- $ ttl = $ this ->redis ->ttl ($ lockKey );
297- assert (is_int ($ ttl ));
317+ $ result = $ this ->redis ->set (
318+ $ lockKey ,
319+ (string ) Time::now ()->getTimestamp (),
320+ // NX -- Only set the key if it does not already exist.
321+ // EX seconds -- Set the specified expire time, in seconds.
322+ ['nx ' , 'ex ' => 300 ]
323+ );
298324
299- if ($ ttl > 0 ) {
300- sleep ( 1 );
325+ if (! $ result ) {
326+ usleep ( 100000 );
301327
302328 continue ;
303329 }
304330
305- if (! $ this ->redis ->setex ($ lockKey , 300 , (string ) Time::now ()->getTimestamp ())) {
306- $ this ->logger ->error ('Session: Error while trying to obtain lock for ' . $ this ->keyPrefix . $ sessionID );
307-
308- return false ;
309- }
310-
311331 $ this ->lockKey = $ lockKey ;
312332 break ;
313- } while (++$ attempt < 30 );
333+ } while (++$ attempt < 300 );
314334
315- if ($ attempt === 30 ) {
316- log_message ('error ' , 'Session: Unable to obtain lock for ' . $ this ->keyPrefix . $ sessionID . ' after 30 attempts, aborting. ' );
335+ if ($ attempt === 300 ) {
336+ $ this ->logger ->error (
337+ 'Session: Unable to obtain lock for ' . $ this ->keyPrefix . $ sessionID
338+ . ' after 300 attempts, aborting. '
339+ );
317340
318341 return false ;
319342 }
320343
321- if ($ ttl === -1 ) {
322- log_message ('debug ' , 'Session: Lock for ' . $ this ->keyPrefix . $ sessionID . ' had no TTL, overriding. ' );
323- }
324-
325344 $ this ->lock = true ;
326345
327346 return true ;
328347 }
329348
330349 /**
331350 * Releases a previously acquired lock
351+ *
352+ * @throws RedisException
332353 */
333354 protected function releaseLock (): bool
334355 {
0 commit comments