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 679eebb

Browse filesBrowse files
bug #25220 [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one (nicolas-grekas)
This PR was merged into the 3.4 branch. Discussion ---------- [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - MockFileSessionStorage should not create any file when the session is empty. Like the native session storage, it should ignore the metadataBag to decide if the session is empty. And to prevent AbstractTestSessionListener from registered a wrong cookie, it must have access to this empty state, which is now possible thanks to a new `Session::isEmpty()` method. Implementing is requires access to the internal storage of bags, which is possible via an internal proxy. Commits ------- 56846ac [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one
2 parents 7582b1a + 56846ac commit 679eebb
Copy full SHA for 679eebb

File tree

8 files changed

+164
-9
lines changed
Filter options

8 files changed

+164
-9
lines changed

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface
2929
protected $options = array(
3030
'check_path' => '/login_check',
3131
'use_forward' => false,
32-
'require_previous_session' => true,
32+
'require_previous_session' => false,
3333
);
3434

3535
protected $defaultSuccessHandlerOptions = array(

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public function __construct()
2828
$this->addOption('password_path', 'password');
2929
$this->defaultFailureHandlerOptions = array();
3030
$this->defaultSuccessHandlerOptions = array();
31-
$this->options['require_previous_session'] = false;
3231
}
3332

3433
/**

‎src/Symfony/Component/HttpFoundation/Session/Session.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Session/Session.php
+21-4Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
2828

2929
private $flashName;
3030
private $attributeName;
31+
private $data = array();
3132

3233
/**
3334
* @param SessionStorageInterface $storage A SessionStorageInterface instance
@@ -108,7 +109,7 @@ public function remove($name)
108109
*/
109110
public function clear()
110111
{
111-
$this->storage->getBag($this->attributeName)->clear();
112+
$this->getAttributeBag()->clear();
112113
}
113114

114115
/**
@@ -139,6 +140,22 @@ public function count()
139140
return count($this->getAttributeBag()->all());
140141
}
141142

143+
/**
144+
* @return bool
145+
*
146+
* @internal
147+
*/
148+
public function isEmpty()
149+
{
150+
foreach ($this->data as &$data) {
151+
if (!empty($data)) {
152+
return false;
153+
}
154+
}
155+
156+
return true;
157+
}
158+
142159
/**
143160
* {@inheritdoc}
144161
*/
@@ -210,15 +227,15 @@ public function getMetadataBag()
210227
*/
211228
public function registerBag(SessionBagInterface $bag)
212229
{
213-
$this->storage->registerBag($bag);
230+
$this->storage->registerBag(new SessionBagProxy($bag, $this->data));
214231
}
215232

216233
/**
217234
* {@inheritdoc}
218235
*/
219236
public function getBag($name)
220237
{
221-
return $this->storage->getBag($name);
238+
return $this->storage->getBag($name)->getBag();
222239
}
223240

224241
/**
@@ -240,6 +257,6 @@ public function getFlashBag()
240257
*/
241258
private function getAttributeBag()
242259
{
243-
return $this->storage->getBag($this->attributeName);
260+
return $this->storage->getBag($this->attributeName)->getBag();
244261
}
245262
}
+79Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\Session;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*
17+
* @internal
18+
*/
19+
final class SessionBagProxy implements SessionBagInterface
20+
{
21+
private $bag;
22+
private $data;
23+
24+
public function __construct(SessionBagInterface $bag, array &$data)
25+
{
26+
$this->bag = $bag;
27+
$this->data = &$data;
28+
}
29+
30+
/**
31+
* @return SessionBagInterface
32+
*/
33+
public function getBag()
34+
{
35+
return $this->bag;
36+
}
37+
38+
/**
39+
* @return bool
40+
*/
41+
public function isEmpty()
42+
{
43+
return empty($this->data[$this->bag->getStorageKey()]);
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
public function getName()
50+
{
51+
return $this->bag->getName();
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function initialize(array &$array)
58+
{
59+
$this->data[$this->bag->getStorageKey()] = &$array;
60+
61+
$this->bag->initialize($array);
62+
}
63+
64+
/**
65+
* {@inheritdoc}
66+
*/
67+
public function getStorageKey()
68+
{
69+
return $this->bag->getStorageKey();
70+
}
71+
72+
/**
73+
* {@inheritdoc}
74+
*/
75+
public function clear()
76+
{
77+
return $this->bag->clear();
78+
}
79+
}

‎src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php
+20-1Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,26 @@ public function save()
9191
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
9292
}
9393

94-
file_put_contents($this->getFilePath(), serialize($this->data));
94+
$data = $this->data;
95+
96+
foreach ($this->bags as $bag) {
97+
if (empty($data[$key = $bag->getStorageKey()])) {
98+
unset($data[$key]);
99+
}
100+
}
101+
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) {
102+
unset($data[$key]);
103+
}
104+
105+
try {
106+
if ($data) {
107+
file_put_contents($this->getFilePath(), serialize($data));
108+
} else {
109+
$this->destroy();
110+
}
111+
} finally {
112+
$this->data = $data;
113+
}
95114

96115
// this is needed for Silex, where the session object is re-used across requests
97116
// in functional tests. In Symfony, the container is rebooted, so we don't have

‎src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,22 @@ public function testGetMeta()
221221
{
222222
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag());
223223
}
224+
225+
public function testIsEmpty()
226+
{
227+
$this->assertTrue($this->session->isEmpty());
228+
229+
$this->session->set('hello', 'world');
230+
$this->assertFalse($this->session->isEmpty());
231+
232+
$this->session->remove('hello');
233+
$this->assertTrue($this->session->isEmpty());
234+
235+
$flash = $this->session->getFlashBag();
236+
$flash->set('hello', 'world');
237+
$this->assertFalse($this->session->isEmpty());
238+
239+
$flash->get('hello');
240+
$this->assertTrue($this->session->isEmpty());
241+
}
224242
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
+5-2Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpKernel\EventListener;
1313

1414
use Symfony\Component\HttpFoundation\Cookie;
15+
use Symfony\Component\HttpFoundation\Session\Session;
1516
use Symfony\Component\HttpFoundation\Session\SessionInterface;
1617
use Symfony\Component\HttpKernel\KernelEvents;
1718
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
@@ -60,8 +61,10 @@ public function onKernelResponse(FilterResponseEvent $event)
6061
$session = $event->getRequest()->getSession();
6162
if ($session && $session->isStarted()) {
6263
$session->save();
63-
$params = session_get_cookie_params();
64-
$event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']));
64+
if (!$session instanceof Session || !\method_exists($session, 'isEmpty') || !$session->isEmpty()) {
65+
$params = session_get_cookie_params();
66+
$event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']));
67+
}
6568
}
6669
}
6770

‎src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,19 @@ public function testDoesNotDeleteCookieIfUsingSessionLifetime()
7373
$this->assertEquals(0, reset($cookies)->getExpiresTime());
7474
}
7575

76+
/**
77+
* @requires function \Symfony\Component\HttpFoundation\Session\Session::isEmpty
78+
*/
79+
public function testEmptySessionDoesNotSendCookie()
80+
{
81+
$this->sessionHasBeenStarted();
82+
$this->sessionIsEmpty();
83+
84+
$response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST);
85+
86+
$this->assertSame(array(), $response->headers->getCookies());
87+
}
88+
7689
public function testUnstartedSessionIsNotSave()
7790
{
7891
$this->sessionHasNotBeenStarted();
@@ -130,6 +143,13 @@ private function sessionHasNotBeenStarted()
130143
->will($this->returnValue(false));
131144
}
132145

146+
private function sessionIsEmpty()
147+
{
148+
$this->session->expects($this->once())
149+
->method('isEmpty')
150+
->will($this->returnValue(true));
151+
}
152+
133153
private function getSession()
134154
{
135155
$mock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')

0 commit comments

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