Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ab83e8a

Browse filesBrowse files
Fix session cookie handling in cli context
1 parent 38cb35a commit ab83e8a
Copy full SHA for ab83e8a

File tree

5 files changed

+88
-5
lines changed
Filter options

5 files changed

+88
-5
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
+2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Request.php
+10-2Lines changed: 10 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,12 @@ 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+
$sessionId = $this->cookies->get($session->getName(), '');
728+
$session->setId($sessionId);
723729
}
724730

725731
if (null === $session) {
@@ -763,6 +769,8 @@ public function setSession(SessionInterface $session)
763769

764770
/**
765771
* @internal
772+
*
773+
* @param callable(): SessionInterface $factory
766774
*/
767775
public function setSessionFactory(callable $factory)
768776
{

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
+45-1Lines changed: 45 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,39 @@ 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+
$popSessionCookie = SessionUtils::popSessionCookie($sessionName, $sessionId);
138+
139+
if (0 === \strpos($popSessionCookie, \sprintf('Set-Cookie: %s=deleted;', $sessionName))) {
140+
$response->headers->removeCookie($sessionName, $sessionCookiePath);
141+
} elseif ($popSessionCookie) {
142+
$expire = 0;
143+
$lifetime = $this->sessionOptions['cookie_lifetime'] ?? null;
144+
if ($lifetime) {
145+
$expire = time() + $lifetime;
146+
}
147+
148+
$response->headers->setCookie(
149+
Cookie::create(
150+
$sessionName,
151+
$sessionId,
152+
$expire,
153+
$sessionCookiePath,
154+
$this->sessionOptions['cookie_domain'] ?? null,
155+
$this->sessionOptions['cookie_secure'] ?? null,
156+
$this->sessionOptions['cookie_httponly'] ?? true,
157+
false,
158+
$this->sessionOptions['cookie_samesite'] ?? Cookie::SAMESITE_LAX
159+
)
160+
);
161+
}
118162
}
119163
}
120164

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/EventListener/SessionListener.php
+5-2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
+26Lines 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
public function testUninitializedSession()
124150
{
125151
$kernel = $this->createMock(HttpKernelInterface::class);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.