8000 Fix session cookie handling in cli context · symfony/symfony@647a4e0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 647a4e0

Browse files
Fix session cookie handling in cli context
1 parent 38cb35a commit 647a4e0

File tree

5 files changed

+105
-5
lines changed

5 files changed

+105
-5
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
<argument key="session" type="service" id="session" on-invalid="ignore" />
7171
<argument key="initialized_session" type="service" id="session" on-invalid="ignore_uninitialized" />
7272
</argument>
73+
<argument>%kernel.debug%</argument>
74+
<argument>%session.storage.options%</argument>
7375
</service>
7476

7577
<service id="session.save_listener" class="Symfony\Component\HttpKernel\EventListener\SaveSessionListener">

src/Symfony/Component/HttpFoundation/Request.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class Request
179179
protected $format;
180180

181181
/**
182-
* @var SessionInterface|callable
182+
* @var SessionInterface|callable(): SessionInterface
183183
*/
184184
protected $session;
185185

@@ -418,7 +418,7 @@ public static function create($uri, $method = 'GET', $parameters = [], $cookies
418418
* to keep BC with an existing system. It should not be used for any
419419
* other purpose.
420420
*
421-
* @param callable|null $callable A PHP callable
421+
* @param callable():SessionInterface|null $callable A PHP callable
422422
*/
423423
public static function setFactory($callable)
424424
{
@@ -720,6 +720,14 @@ public function getSession()
720720
$session = $this->session;
721721
if (!$session instanceof SessionInterface && null !== $session) {
722722
$this->setSession($session = $session());
723+
/*
724+
* For supporting sessions in php runtime with runners like roadrunner or swoole the session
725+
* cookie need read from the cookie bag and set on the session storage.
726+
*/
727+
if (!$session->isStarted()) {
728+
$sessionId = $this->cookies->get($session->getName(), '');
729+
$session->setId($sessionId);
730+
}
723731
}
724732

725733
if (null === $session) {
@@ -763,6 +771,8 @@ public function setSession(SessionInterface $session)
763771

764772
/**
765773
* @internal
774+
*
775+
* @param callable(): SessionInterface $factory
766776
*/
767777
public function setSessionFactory(callable $factory)
768778
{

src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313

1414
use Psr\Container\ContainerInterface;
1515
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16+
use Symfony\Component\HttpFoundation\Cookie;
1617
use Symfony\Component\HttpFoundation\Session\Session;
1718
use Symfony\Component\HttpFoundation\Session\SessionInterface;
19+
use Symfony\Component\HttpFoundation\Session\SessionUtils;
1820
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
1921
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
2022
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -42,9 +44,18 @@ abstract class AbstractSessionListener implements EventSubscriberInterface
4244
protected $container;
4345
private $sessionUsageStack = [];
4446

45-
public function __construct(ContainerInterface $container = null)
47+
/**
48+
* @var array<string, mixed>
49+
*/
50+
private $sessionOptions;
51+
52+
/**
53+
* @param array<string, mixed> $sessionOptions
54+
*/
55+
public function __construct(ContainerInterface $container = null, bool $debug = false, array $sessionOptions = [])
4656
{
4757
$this->container = $container;
58+
$this->sessionOptions = $sessionOptions;
4859
}
4960

5061
public function onKernelRequest(GetResponseEvent $event)
@@ -115,6 +126,54 @@ public function onKernelResponse(FilterResponseEvent $event)
115126
* it is saved will just restart it.
116127
*/
117128
$session->save();
129+
130+
/*
131+
* For supporting sessions in php runtime with runners like roadrunner or swoole the session
132+
* cookie need to be written on the response object and should not be written by PHP itself.
133+
*/
134+
$sessionName = $session->getName();
135+
$sessionId = $session->getId();
136+
$sessionCookiePath = $this->sessionOptions['cookie_path'] ?? '/';
137+
$sessionCookieDomain = $this->sessionOptions['cookie_domain'] ?? null;
138+
$sessionCookieSecure = $this->sessionOptions['cookie_secure'] ?? null;
139+
$sessionCookieHttpOnly = $this->sessionOptions['cookie_httponly'] ?? true;
140+
$sessionCookieSameSite = $this->sessionOptions['cookie_samesite'] ?? Cookie::SAMESITE_LAX;
141+
142+
SessionUtils::popSessionCookie($sessionName, $sessionCookiePath);
143+
144+
$request = $event->getRequest();
145+
$requestSessionCoo A340 kieId = $request->cookies->get($sessionName);
146+
147+
if ($requestSessionCookieId && $session->isEmpty()) {
148+
$response->headers->clearCookie(
149+
$sessionName,
150+
$sessionCookiePath,
151+
$sessionCookieDomain,
152+
$sessionCookieSecure,
153+
$sessionCookieHttpOnly,
154+
$sessionCookieSameSite
155+
);
156+
} elseif ($sessionId !== $requestSessionCookieId) {
157+
$expire = 0;
158+
$lifetime = $this->sessionOptions['cookie_lifetime'] ?? null;
159+
if ($lifetime) {
160+
$expire = time() + $lifetime;
161+
}
162+
163+
$response->headers->setCookie(
164+
Cookie::create(
165+
$sessionName,
166+
$sessionId,
167+
$expire,
168+
$sessionCookiePath,
169+
$sessionCookieDomain,
170+
$sessionCookieSecure,
171+
$sessionCookieHttpOnly,
172+
false,
173+
$sessionCookieSameSite
174+
)
175+
);
176+
}
118177
}
119178
}
120179

src/Symfony/Component/HttpKernel/EventListener/SessionListener.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@
2929
*/
3030
class SessionListener extends AbstractSessionListener
3131
{
32-
public function __construct(ContainerInterface $container)
32+
/**
33+
* @param array<string, mixed> $sessionOptions
34+
*/
35+
public function __construct(ContainerInterface $container, bool $debug = false, array $sessionOptions = [])
3336
{
34-
$this->container = $container;
37+
parent::__construct($container, $debug, $sessionOptions);
3538
}
3639

3740
public function onKernelRequest(GetResponseEvent $event)

src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@ public function testResponseIsStillPublicIfSessionStartedAndHeaderPresent()
120120
$this->assertFalse($response->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER));
121121
}
122122

123+
public function testSessionSaveAndResponseHasSessionCookie()
124+
{
125+
$session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock();
126+
$session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1));
127+
$session->expects($this->exactly(1))->method('getId')->willReturn('123456');
128+
$session->expects($this->exactly(1))->method('getName')->willReturn('PHPSESSID');
129+
$session->expects($this->exactly(1))->method('save');
130+
$session->expects($this->exactly(1))->method('isStarted')->willReturn(true);
131+
132+
$container = new Container();
133+
$container->set('initialized_session', $session);
134+
135+
$listener = new SessionListener($container);
136+
$kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock();
137+
138+
$request = new Request();
139+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST));
140+
141+
$response = new Response();
142+
$listener->onKernelResponse(new ResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response));
143+
144+
$cookies = $response->headers->getCookies();
145+
$this->assertSame('PHPSESSID', $cookies[0]->getName());
146+
$this->assertSame('123456', $cookies[0]->getValue());
147+
}
148+
123149
publ 43B6 ic function testUninitializedSession()
124150
{
125151
$kernel = $this->createMock(HttpKernelInterface::class);

0 commit comments

Comments
 (0)
0