Skip to content

Commit eaa0b1a

Browse files
alexander-schranznicolas-grekas
authored andcommitted
[HttpKernel] Fix compatibility with php bridge and already started php sessions
1 parent 2e1c19e commit eaa0b1a

File tree

2 files changed

+192
-5
lines changed

2 files changed

+192
-5
lines changed

EventListener/AbstractSessionListener.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,12 @@ public function onKernelRequest(RequestEvent $event)
7575
}
7676

7777
/*
78-
* For supporting sessions in php runtime with runners like roadrunner or swoole the session
79-
* cookie need read from the cookie bag and set on the session storage.
78+
* For supporting sessions in php runtime with runners like roadrunner or swoole, the session
79+
* cookie needs to be read from the cookie bag and set on the session storage.
80+
*
81+
* Do not set it when a native php session is active.
8082
*/
81-
if ($sess && !$sess->isStarted()) {
83+
if ($sess && !$sess->isStarted() && \PHP_SESSION_ACTIVE !== session_status()) {
8284
$sessionId = $request->cookies->get($sess->getName(), '');
8385
$sess->setId($sessionId);
8486
}
@@ -152,7 +154,8 @@ public function onKernelResponse(ResponseEvent $event)
152154
$request = $event->getRequest();
153155
$requestSessionCookieId = $request->cookies->get($sessionName);
154156

155-
if ($requestSessionCookieId && $session->isEmpty()) {
157+
$isSessionEmpty = $session->isEmpty() && empty($_SESSION); // checking $_SESSION to keep compatibility with native sessions
158+
if ($requestSessionCookieId && $isSessionEmpty) {
156159
$response->headers->clearCookie(
157160
$sessionName,
158161
$sessionCookiePath,
@@ -161,7 +164,7 @@ public function onKernelResponse(ResponseEvent $event)
161164
$sessionCookieHttpOnly,
162165
$sessionCookieSameSite
163166
);
164-
} elseif ($sessionId !== $requestSessionCookieId) {
167+
} elseif ($sessionId !== $requestSessionCookieId && !$isSessionEmpty) {
165168
$expire = 0;
166169
$lifetime = $sessionOptions['cookie_lifetime'] ?? null;
167170
if ($lifetime) {

Tests/EventListener/SessionListenerTest.php

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\HttpFoundation\Session\Session;
2323
use Symfony\Component\HttpFoundation\Session\SessionFactory;
2424
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
25+
use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
2526
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
2627
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
2728
use Symfony\Component\HttpKernel\Event\RequestEvent;
@@ -125,6 +126,189 @@ public function provideSessionOptions(): \Generator
125126
];
126127
}
127128

129+
/**
130+
* @runInSeparateProcess
131+
*/
132+
public function testPhpBridgeAlreadyStartedSession()
133+
{
134+
session_start();
135+
$sessionId = session_id();
136+
137+
$requestStack = new RequestStack();
138+
$request = new Request();
139+
$requestStack->push($request);
140+
141+
$session = new Session();
142+
$sessionStorage = new PhpBridgeSessionStorage();
143+
144+
$container = new Container();
145+
$container->set('request_stack', $requestStack);
146+
$container->set('session', $session);
147+
$container->set('session_storage', $sessionStorage);
148+
149+
$request = new Request();
150+
$listener = new SessionListener($container);
151+
152+
$event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST);
153+
154+
$listener->onKernelRequest($event);
155+
156+
$this->assertTrue($request->hasSession());
157+
$this->assertSame($sessionId, $request->getSession()->getId());
158+
}
159+
160+
/**
161+
* @runInSeparateProcess
162+
*/
163+
public function testSessionCookieWrittenNoCookieGiven()
164+
{
165+
$session = new Session();
166+
$session->set('hello', 'world');
167+
168+
$container = new Container();
169+
$container->set('initialized_session', $session);
170+
171+
$listener = new SessionListener($container);
172+
$kernel = $this->createMock(HttpKernelInterface::class);
173+
174+
$request = new Request();
175+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST));
176+
177+
$response = new Response();
178+
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response));
179+
180+
$cookies = $response->headers->getCookies();
181+
$this->assertCount(1, $cookies);
182+
$sessionCookie = $cookies[0];
183+
184+
$this->assertSame('PHPSESSID', $sessionCookie->getName());
185+
$this->assertNotEmpty($sessionCookie->getValue());
186+
$this->assertFalse($sessionCookie->isCleared());
187+
}
188+
189+
/**
190+
* @runInSeparateProcess
191+
*/
192+
public function testSessionCookieNotWrittenCookieGiven()
193+
{
194+
$session = new Session();
195+
$session->set('hello', 'world');
196+
$sessionId = $session->getId();
197+
198+
$container = new Container();
199+
$container->set('initialized_session', $session);
200+
201+
$listener = new SessionListener($container);
202+
$kernel = $this->createMock(HttpKernelInterface::class);
203+
204+
$request = new Request();
205+
$request->cookies->set('PHPSESSID', $sessionId);
206+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST));
207+
208+
$response = new Response();
209+
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response));
210+
211+
$cookies = $response->headers->getCookies();
212+
$this->assertCount(0, $cookies);
213+
}
214+
215+
/**
216+
* @runInSeparateProcess
217+
*/
218+
public function testSessionCookieClearedWhenInvalidated()
219+
{
220+
$session = new Session();
221+
222+
$container = new Container();
223+
$container->set('initialized_session', $session);
224+
225+
$listener = new SessionListener($container);
226+
$kernel = $this->createMock(HttpKernelInterface::class);
227+
228+
$request = new Request();
229+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST));
230+
231+
$session->start();
232+
$sessionId = $session->getId();
233+
$this->assertNotEmpty($sessionId);
234+
$request->cookies->set($session->getName(), $sessionId);
235+
$_SESSION['hello'] = 'world'; // check compatibility to php session bridge
236+
237+
$session->invalidate();
238+
239+
$response = new Response();
240+
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response));
241+
242+
$cookies = $response->headers->getCookies();
243+
$this->assertCount(1, $cookies);
244+
$sessionCookie = $cookies[0];
245+
246+
$this->assertSame('PHPSESSID', $sessionCookie->getName());
247+
$this->assertTrue($sessionCookie->isCleared());
248+
}
249+
250+
/**
251+
* @runInSeparateProcess
252+
*/
253+
public function testSessionCookieNotClearedWhenOtherVariablesSet()
254+
{
255+
$session = new Session();
256+
257+
$container = new Container();
258+
$container->set('initialized_session', $session);
259+
260+
$listener = new SessionListener($container);
261+
$kernel = $this->createMock(HttpKernelInterface::class);
262+
263+
$request = new Request();
264+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST));
265+
266+
$session->start();
267+
$sessionId = $session->getId();
268+
$this->assertNotEmpty($sessionId);
269+
$request->cookies->set($session->getName(), $sessionId);
270+
$_SESSION['hello'] = 'world';
271+
272+
$response = new Response();
273+
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response));
274+
275+
$cookies = $response->headers->getCookies();
276+
$this->assertCount(0, $cookies);
277+
}
278+
279+
/**
280+
* @runInSeparateProcess
281+
*/
282+
public function testSessionCookieSetWhenOtherNativeVariablesSet()
283+
{
284+
$session = new Session();
285+
286+
$container = new Container();
287+
$container->set('initialized_session', $session);
288+
289+
$listener = new SessionListener($container);
290+
$kernel = $this->createMock(HttpKernelInterface::class);
291+
292+
$request = new Request();
293+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST));
294+
295+
$session->start();
296+
$sessionId = $session->getId();
297+
$this->assertNotEmpty($sessionId);
298+
$_SESSION['hello'] = 'world';
299+
300+
$response = new Response();
301+
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response));
302+
303+
$cookies = $response->headers->getCookies();
304+
$this->assertCount(1, $cookies);
305+
$sessionCookie = $cookies[0];
306+
307+
$this->assertSame('PHPSESSID', $sessionCookie->getName());
308+
$this->assertNotEmpty($sessionCookie->getValue());
309+
$this->assertFalse($sessionCookie->isCleared());
310+
}
311+
128312
public function testOnlyTriggeredOnMainRequest()
129313
{
130314
$listener = $this->getMockForAbstractClass(AbstractSessionListener::class);

0 commit comments

Comments
 (0)