From 018fb549a630016ac696d2c98acfcb2378f23cd6 Mon Sep 17 00:00:00 2001 From: klsoft-web Date: Sun, 13 Sep 2026 14:57:52 +0300 Subject: [PATCH 01/10] Add the createId() method of SessionInterface and then use it in SessionMiddleware when requestSessionId is null --- CHANGELOG.md | 1 + src/NullSession.php | 9 +++++++++ src/Session.php | 13 +++++++++++++ src/SessionInterface.php | 5 +++++ src/SessionMiddleware.php | 4 ++-- tests/Flash/MockArraySessionStorage.php | 5 +++++ tests/SessionTest.php | 5 +++++ 7 files changed, 40 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af09c2..a047c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Enh #86: Remove `yiisoft/cookies` dependency (@vjik) - Bug #86: `NullSession::getCookieParameters()` now returns proper cookie parameters instead of an empty array (@vjik) +- Bug #25: To prevent the same session ID being received for both Swoole and Road Runner, add the `createId()` method of `SessionInterface` and then use it in `SessionMiddleware` when `requestSessionId` is `null` (@klsoft-web) ## 3.0.2 August 26, 2026 diff --git a/src/NullSession.php b/src/NullSession.php index 64dd5dc..66de145 100644 --- a/src/NullSession.php +++ b/src/NullSession.php @@ -27,6 +27,15 @@ public function isActive(): bool public function regenerateId(): void {} + public function createId(): string { + $sessionId = session_create_id(); + if (!$sessionId) { + throw new SessionException('Failed to create ID.'); + } + + return $sessionId; + } + public function discard(): void {} public function all(): array diff --git a/src/Session.php b/src/Session.php index c272959..a8a8e3c 100644 --- a/src/Session.php +++ b/src/Session.php @@ -133,6 +133,19 @@ public function regenerateId(): void } } + /** + * @throw SessionException When create session id is failed. + */ + public function createId(): string + { + $sessionId = session_create_id(); + if (!$sessionId) { + throw new SessionException('Failed to create ID.'); + } + + return $sessionId; + } + public function discard(): void { if ($this->isActive()) { diff --git a/src/SessionInterface.php b/src/SessionInterface.php index ea53a5a..66eb810 100644 --- a/src/SessionInterface.php +++ b/src/SessionInterface.php @@ -67,6 +67,11 @@ public function setId(string $sessionId): void; */ public function regenerateId(): void; + /** + * Create session ID. + */ + public function createId(): string; + /** * Discard session changes and close session. */ diff --git a/src/SessionMiddleware.php b/src/SessionMiddleware.php index af2bbab..391abe7 100644 --- a/src/SessionMiddleware.php +++ b/src/SessionMiddleware.php @@ -28,8 +28,8 @@ public function __construct(private SessionInterface $session) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $requestSessionId = $this->getSessionIdFromRequest($request); - if ($requestSessionId !== null && $this->session->getId() === null) { - $this->session->setId($requestSessionId); + if ($this->session->getId() === null) { + $this->session->setId($requestSessionId ?? $this->session->createId()); } try { diff --git a/tests/Flash/MockArraySessionStorage.php b/tests/Flash/MockArraySessionStorage.php index bb7ce70..12e5ca1 100644 --- a/tests/Flash/MockArraySessionStorage.php +++ b/tests/Flash/MockArraySessionStorage.php @@ -58,6 +58,11 @@ public function regenerateId(): void $this->id = $this->generateId(); } + public function createId(): string + { + return $this->generateId(); + } + public function discard(): void { $this->close(); diff --git a/tests/SessionTest.php b/tests/SessionTest.php index 5cb781a..3f6d22d 100644 --- a/tests/SessionTest.php +++ b/tests/SessionTest.php @@ -94,6 +94,11 @@ public function testRegenerateIdOpensInactiveSession(): void self::assertNotEquals($id, $session->getId()); } + public function testCreateID(): void + { + self::assertNotEquals($this->getSession()->createId(), ''); + } + public function testDiscard(): void { $session = $this->getSession(); From 25a0b456306f809731ac264191ea087c98f2f1d7 Mon Sep 17 00:00:00 2001 From: klsoft-web Date: Fri, 18 Sep 2026 15:27:35 +0300 Subject: [PATCH 02/10] Refactoring --- src/NullSession.php | 7 +------ src/Session.php | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/NullSession.php b/src/NullSession.php index 66de145..c991cb6 100644 --- a/src/NullSession.php +++ b/src/NullSession.php @@ -28,12 +28,7 @@ public function isActive(): bool public function regenerateId(): void {} public function createId(): string { - $sessionId = session_create_id(); - if (!$sessionId) { - throw new SessionException('Failed to create ID.'); - } - - return $sessionId; + return ''; } public function discard(): void {} diff --git a/src/Session.php b/src/Session.php index a8a8e3c..f159ac9 100644 --- a/src/Session.php +++ b/src/Session.php @@ -84,7 +84,7 @@ public function close(): void } /** - * @throw SessionException When start session is failed. + * @throws SessionException When start session is failed. */ public function open(): void { @@ -134,7 +134,7 @@ public function regenerateId(): void } /** - * @throw SessionException When create session id is failed. + * @throws SessionException When create session id is failed. */ public function createId(): string { From b9ca1f3683982b1a72ff46fee9ce8a15d4118e07 Mon Sep 17 00:00:00 2001 From: klsoft-web Date: Thu, 24 Sep 2026 12:59:57 +0300 Subject: [PATCH 03/10] The Session::createId() call has been moved into the Session::open() method --- src/Session.php | 4 +--- src/SessionMiddleware.php | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Session.php b/src/Session.php index f159ac9..590cceb 100644 --- a/src/Session.php +++ b/src/Session.php @@ -92,9 +92,7 @@ public function open(): void return; } - if ($this->sessionId !== null) { - session_id($this->sessionId); - } + session_id($this->sessionId ?? $this->createId()); try { session_start($this->options); diff --git a/src/SessionMiddleware.php b/src/SessionMiddleware.php index 391abe7..af2bbab 100644 --- a/src/SessionMiddleware.php +++ b/src/SessionMiddleware.php @@ -28,8 +28,8 @@ public function __construct(private SessionInterface $session) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $requestSessionId = $this->getSessionIdFromRequest($request); - if ($this->session->getId() === null) { - $this->session->setId($requestSessionId ?? $this->session->createId()); + if ($requestSessionId !== null && $this->session->getId() === null) { + $this->session->setId($requestSessionId); } try { From 54b98eba68f6bdaf93faf0bbb4d1f0f8625fc794 Mon Sep 17 00:00:00 2001 From: klsoft-web Date: Thu, 24 Sep 2026 19:23:33 +0300 Subject: [PATCH 04/10] The createId() method has been removed from the SessionInterface and made a private method of the Session class instead --- CHANGELOG.md | 2 +- src/NullSession.php | 4 ---- src/Session.php | 2 +- src/SessionInterface.php | 5 ----- tests/Flash/MockArraySessionStorage.php | 5 ----- tests/SessionTest.php | 5 ----- 6 files changed, 2 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a047c4b..5e16982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Enh #86: Remove `yiisoft/cookies` dependency (@vjik) - Bug #86: `NullSession::getCookieParameters()` now returns proper cookie parameters instead of an empty array (@vjik) -- Bug #25: To prevent the same session ID being received for both Swoole and Road Runner, add the `createId()` method of `SessionInterface` and then use it in `SessionMiddleware` when `requestSessionId` is `null` (@klsoft-web) +- Bug #25: To prevent the same session ID being received for both Swoole and Road Runner, add the private `createId()` method of `Session` and then use it when the `sessionId` is `null` (@klsoft-web) ## 3.0.2 August 26, 2026 diff --git a/src/NullSession.php b/src/NullSession.php index c991cb6..64dd5dc 100644 --- a/src/NullSession.php +++ b/src/NullSession.php @@ -27,10 +27,6 @@ public function isActive(): bool public function regenerateId(): void {} - public function createId(): string { - return ''; - } - public function discard(): void {} public function all(): array diff --git a/src/Session.php b/src/Session.php index 590cceb..37fbfd2 100644 --- a/src/Session.php +++ b/src/Session.php @@ -134,7 +134,7 @@ public function regenerateId(): void /** * @throws SessionException When create session id is failed. */ - public function createId(): string + private function createId(): string { $sessionId = session_create_id(); if (!$sessionId) { diff --git a/src/SessionInterface.php b/src/SessionInterface.php index 66eb810..ea53a5a 100644 --- a/src/SessionInterface.php +++ b/src/SessionInterface.php @@ -67,11 +67,6 @@ public function setId(string $sessionId): void; */ public function regenerateId(): void; - /** - * Create session ID. - */ - public function createId(): string; - /** * Discard session changes and close session. */ diff --git a/tests/Flash/MockArraySessionStorage.php b/tests/Flash/MockArraySessionStorage.php index 12e5ca1..bb7ce70 100644 --- a/tests/Flash/MockArraySessionStorage.php +++ b/tests/Flash/MockArraySessionStorage.php @@ -58,11 +58,6 @@ public function regenerateId(): void $this->id = $this->generateId(); } - public function createId(): string - { - return $this->generateId(); - } - public function discard(): void { $this->close(); diff --git a/tests/SessionTest.php b/tests/SessionTest.php index 3f6d22d..5cb781a 100644 --- a/tests/SessionTest.php +++ b/tests/SessionTest.php @@ -94,11 +94,6 @@ public function testRegenerateIdOpensInactiveSession(): void self::assertNotEquals($id, $session->getId()); } - public function testCreateID(): void - { - self::assertNotEquals($this->getSession()->createId(), ''); - } - public function testDiscard(): void { $session = $this->getSession(); From 383bf3318dfb8750882c1319179e53825f45ba9d Mon Sep 17 00:00:00 2001 From: klsoft-web Date: Fri, 25 Sep 2026 18:10:47 +0300 Subject: [PATCH 05/10] Update CHANGELOG.md Co-authored-by: Sergei Predvoditelev --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e16982..192df8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Enh #86: Remove `yiisoft/cookies` dependency (@vjik) - Bug #86: `NullSession::getCookieParameters()` now returns proper cookie parameters instead of an empty array (@vjik) -- Bug #25: To prevent the same session ID being received for both Swoole and Road Runner, add the private `createId()` method of `Session` and then use it when the `sessionId` is `null` (@klsoft-web) +- Bug #88: Fix reusing the previous session ID in `Session::open()` in long-running workers (@klsoft-web) ## 3.0.2 August 26, 2026 From aed6fb6b064be24cfd775b38e33f007ed1431fb5 Mon Sep 17 00:00:00 2001 From: klsoft-web Date: Fri, 25 Sep 2026 18:16:46 +0300 Subject: [PATCH 06/10] The test testCreateIdWhenSessionIdIsNull() has been added --- tests/SessionTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/SessionTest.php b/tests/SessionTest.php index 5cb781a..15234e9 100644 --- a/tests/SessionTest.php +++ b/tests/SessionTest.php @@ -94,6 +94,15 @@ public function testRegenerateIdOpensInactiveSession(): void self::assertNotEquals($id, $session->getId()); } + public function testCreateIdWhenSessionIdIsNull(): void + { + $session = $this->getSession(); + $initialId = $session->getId(); + $session->open(); + self::assertNull($initialId); + self::assertNotNull($session->getId()); + } + public function testDiscard(): void { $session = $this->getSession(); From 73a3a19bcc177d32aa3509283879d0317b030254 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 25 Sep 2026 20:30:24 +0300 Subject: [PATCH 07/10] improve --- src/Session.php | 28 +++++++++++++++------------- tests/SessionTest.php | 16 ++++++++++------ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/Session.php b/src/Session.php index 37fbfd2..7b7d691 100644 --- a/src/Session.php +++ b/src/Session.php @@ -131,19 +131,6 @@ public function regenerateId(): void } } - /** - * @throws SessionException When create session id is failed. - */ - private function createId(): string - { - $sessionId = session_create_id(); - if (!$sessionId) { - throw new SessionException('Failed to create ID.'); - } - - return $sessionId; - } - public function discard(): void { if ($this->isActive()) { @@ -231,4 +218,19 @@ public function setId(string $sessionId): void { $this->sessionId = $sessionId; } + + /** + * @throws SessionException When create session id is failed. + */ + private function createId(): string + { + $sessionId = session_create_id(); + if (!$sessionId) { + // @codeCoverageIgnoreStart + throw new SessionException('Failed to create ID.'); + // @codeCoverageIgnoreEnd + } + + return $sessionId; + } } diff --git a/tests/SessionTest.php b/tests/SessionTest.php index 15234e9..ca90c96 100644 --- a/tests/SessionTest.php +++ b/tests/SessionTest.php @@ -94,13 +94,17 @@ public function testRegenerateIdOpensInactiveSession(): void self::assertNotEquals($id, $session->getId()); } - public function testCreateIdWhenSessionIdIsNull(): void + public function testOpenDoesNotReuseIdOfClosedSession(): void { - $session = $this->getSession(); - $initialId = $session->getId(); - $session->open(); - self::assertNull($initialId); - self::assertNotNull($session->getId()); + $firstSession = $this->getSession(); + $firstSession->open(); + $firstId = $firstSession->getId(); + $firstSession->close(); + + $this->session = new Session(); + $this->session->open(); + + self::assertNotSame($firstId, $this->session->getId()); } public function testDiscard(): void From 667b0ce63347f4a15d0b72c97fe22b90e3175e12 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 25 Sep 2026 20:34:58 +0300 Subject: [PATCH 08/10] improve --- tests/SessionTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/SessionTest.php b/tests/SessionTest.php index ca90c96..a33988f 100644 --- a/tests/SessionTest.php +++ b/tests/SessionTest.php @@ -96,15 +96,15 @@ public function testRegenerateIdOpensInactiveSession(): void public function testOpenDoesNotReuseIdOfClosedSession(): void { - $firstSession = $this->getSession(); + $firstSession = new Session(); $firstSession->open(); $firstId = $firstSession->getId(); $firstSession->close(); - $this->session = new Session(); - $this->session->open(); + $secondSession = new Session(); + $secondSession->open(); - self::assertNotSame($firstId, $this->session->getId()); + self::assertNotSame($firstId, $secondSession->getId()); } public function testDiscard(): void From 7ef93c85c41e4cf0d8b5db580cd29839b21eccc6 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 25 Sep 2026 20:35:41 +0300 Subject: [PATCH 09/10] improve --- src/Session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Session.php b/src/Session.php index 7b7d691..dbf3f96 100644 --- a/src/Session.php +++ b/src/Session.php @@ -225,7 +225,7 @@ public function setId(string $sessionId): void private function createId(): string { $sessionId = session_create_id(); - if (!$sessionId) { + if ($sessionId === false) { // @codeCoverageIgnoreStart throw new SessionException('Failed to create ID.'); // @codeCoverageIgnoreEnd From ac95e28e7e93f115146fef17ed1151f5b2c45060 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 25 Sep 2026 20:45:19 +0300 Subject: [PATCH 10/10] fix --- src/Session.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Session.php b/src/Session.php index dbf3f96..8e1ad55 100644 --- a/src/Session.php +++ b/src/Session.php @@ -225,6 +225,7 @@ public function setId(string $sessionId): void private function createId(): string { $sessionId = session_create_id(); + /** @psalm-suppress TypeDoesNotContainType PHP 8.0 stub in Psalm lacks `false` in the return type. */ if ($sessionId === false) { // @codeCoverageIgnoreStart throw new SessionException('Failed to create ID.');