diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index 7f69ed79ccc76..7c0c1aee3ac17 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -75,10 +75,12 @@ public function onKernelRequest(RequestEvent $event) } /* - * For supporting sessions in php runtime with runners like roadrunner or swoole the session - * cookie need read from the cookie bag and set on the session storage. + * For supporting sessions in php runtime with runners like roadrunner or swoole, the session + * cookie needs to be read from the cookie bag and set on the session storage. + * + * Do not set it when a native php session is active. */ - if ($sess && !$sess->isStarted()) { + if ($sess && !$sess->isStarted() && \PHP_SESSION_ACTIVE !== session_status()) { $sessionId = $request->cookies->get($sess->getName(), ''); $sess->setId($sessionId); } @@ -152,7 +154,8 @@ public function onKernelResponse(ResponseEvent $event) $request = $event->getRequest(); $requestSessionCookieId = $request->cookies->get($sessionName); - if ($requestSessionCookieId && $session->isEmpty()) { + $isSessionEmpty = $session->isEmpty() && empty($_SESSION); // checking $_SESSION to keep compatibility with native sessions + if ($requestSessionCookieId && $isSessionEmpty) { $response->headers->clearCookie( $sessionName, $sessionCookiePath, @@ -161,7 +164,7 @@ public function onKernelResponse(ResponseEvent $event) $sessionCookieHttpOnly, $sessionCookieSameSite ); - } elseif ($sessionId !== $requestSessionCookieId) { + } elseif ($sessionId !== $requestSessionCookieId && !$isSessionEmpty) { $expire = 0; $lifetime = $sessionOptions['cookie_lifetime'] ?? null; if ($lifetime) { diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php index 45f479df34d30..3f9f2872edc31 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php @@ -22,6 +22,7 @@ use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\SessionFactory; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -125,6 +126,189 @@ public function provideSessionOptions(): \Generator ]; } + /** + * @runInSeparateProcess + */ + public function testPhpBridgeAlreadyStartedSession() + { + session_start(); + $sessionId = session_id(); + + $requestStack = new RequestStack(); + $request = new Request(); + $requestStack->push($request); + + $session = new Session(); + $sessionStorage = new PhpBridgeSessionStorage(); + + $container = new Container(); + $container->set('request_stack', $requestStack); + $container->set('session', $session); + $container->set('session_storage', $sessionStorage); + + $request = new Request(); + $listener = new SessionListener($container); + + $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); + + $listener->onKernelRequest($event); + + $this->assertTrue($request->hasSession()); + $this->assertSame($sessionId, $request->getSession()->getId()); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieWrittenNoCookieGiven() + { + $session = new Session(); + $session->set('hello', 'world'); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->createMock(HttpKernelInterface::class); + + $request = new Request(); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(1, $cookies); + $sessionCookie = $cookies[0]; + + $this->assertSame('PHPSESSID', $sessionCookie->getName()); + $this->assertNotEmpty($sessionCookie->getValue()); + $this->assertFalse($sessionCookie->isCleared()); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieNotWrittenCookieGiven() + { + $session = new Session(); + $session->set('hello', 'world'); + $sessionId = $session->getId(); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->createMock(HttpKernelInterface::class); + + $request = new Request(); + $request->cookies->set('PHPSESSID', $sessionId); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieClearedWhenInvalidated() + { + $session = new Session(); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->createMock(HttpKernelInterface::class); + + $request = new Request(); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $session->start(); + $sessionId = $session->getId(); + $this->assertNotEmpty($sessionId); + $request->cookies->set($session->getName(), $sessionId); + $_SESSION['hello'] = 'world'; // check compatibility to php session bridge + + $session->invalidate(); + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(1, $cookies); + $sessionCookie = $cookies[0]; + + $this->assertSame('PHPSESSID', $sessionCookie->getName()); + $this->assertTrue($sessionCookie->isCleared()); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieNotClearedWhenOtherVariablesSet() + { + $session = new Session(); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->createMock(HttpKernelInterface::class); + + $request = new Request(); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $session->start(); + $sessionId = $session->getId(); + $this->assertNotEmpty($sessionId); + $request->cookies->set($session->getName(), $sessionId); + $_SESSION['hello'] = 'world'; + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieSetWhenOtherNativeVariablesSet() + { + $session = new Session(); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->createMock(HttpKernelInterface::class); + + $request = new Request(); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $session->start(); + $sessionId = $session->getId(); + $this->assertNotEmpty($sessionId); + $_SESSION['hello'] = 'world'; + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(1, $cookies); + $sessionCookie = $cookies[0]; + + $this->assertSame('PHPSESSID', $sessionCookie->getName()); + $this->assertNotEmpty($sessionCookie->getValue()); + $this->assertFalse($sessionCookie->isCleared()); + } + public function testOnlyTriggeredOnMainRequest() { $listener = $this->getMockForAbstractClass(AbstractSessionListener::class);