diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af09c2..192df8f 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 #88: Fix reusing the previous session ID in `Session::open()` in long-running workers (@klsoft-web) ## 3.0.2 August 26, 2026 diff --git a/src/Session.php b/src/Session.php index c272959..8e1ad55 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 { @@ -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); @@ -220,4 +218,20 @@ 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(); + /** @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.'); + // @codeCoverageIgnoreEnd + } + + return $sessionId; + } } diff --git a/tests/SessionTest.php b/tests/SessionTest.php index 5cb781a..a33988f 100644 --- a/tests/SessionTest.php +++ b/tests/SessionTest.php @@ -94,6 +94,19 @@ public function testRegenerateIdOpensInactiveSession(): void self::assertNotEquals($id, $session->getId()); } + public function testOpenDoesNotReuseIdOfClosedSession(): void + { + $firstSession = new Session(); + $firstSession->open(); + $firstId = $firstSession->getId(); + $firstSession->close(); + + $secondSession = new Session(); + $secondSession->open(); + + self::assertNotSame($firstId, $secondSession->getId()); + } + public function testDiscard(): void { $session = $this->getSession();