Skip to content

Commit 32dfdb3

Browse files
Merge branch '3.4' into 4.0
* 3.4: [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one [HttpKernel] Add a better error messages when passing a private or non-tagged controller [VarDumper] Dont use empty(), it chokes on eg GMP objects [Dotenv] Changed preg_match flags from null to 0 remove upgrade instructions for kernel.root_dir [HttpKernel] Arrays with scalar values passed to ESI fragment renderer throw deprecation notice [HttpKernel] add a test for FilterControllerEvents
2 parents ee71106 + 679eebb commit 32dfdb3

File tree

17 files changed

+259
-24
lines changed

17 files changed

+259
-24
lines changed

UPGRADE-4.0.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -598,12 +598,6 @@ HttpKernel
598598
tags: ['console.command']
599599
```
600600
601-
* Removed the `kernel.root_dir` parameter. Use the `kernel.project_dir` parameter
602-
instead.
603-
604-
* Removed the `Kernel::getRootDir()` method. Use the `Kernel::getProjectDir()`
605-
method instead.
606-
607601
* The `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` methods have been removed.
608602

609603
* Possibility to pass non-scalar values as URI attributes to the ESI and SSI

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

Lines 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

Lines 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/Dotenv/Dotenv.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ private function lexVarname()
174174

175175
private function lexValue()
176176
{
177-
if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, null, $this->cursor)) {
177+
if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, 0, $this->cursor)) {
178178
$this->moveCursor($matches[0]);
179179
$this->skipEmptyLines();
180180

@@ -296,7 +296,7 @@ private function lexNestedExpression()
296296

297297
private function skipEmptyLines()
298298
{
299-
if (preg_match('/(?:\s*+(?:#[^\n]*+)?+)++/A', $this->data, $match, null, $this->cursor)) {
299+
if (preg_match('/(?:\s*+(?:#[^\n]*+)?+)++/A', $this->data, $match, 0, $this->cursor)) {
300300
$this->moveCursor($match[0]);
301301
}
302302
}

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

Lines 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
}
Lines 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 <[email protected]>
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 <[email protected]>
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

Lines 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

Lines 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/Controller/ContainerControllerResolver.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Psr\Container\ContainerInterface;
1515
use Psr\Log\LoggerInterface;
16+
use Symfony\Component\DependencyInjection\Container;
1617
use Symfony\Component\HttpFoundation\Request;
1718

1819
/**
@@ -86,6 +87,15 @@ protected function instantiateController($class)
8687
return $this->container->get($class);
8788
}
8889

89-
return parent::instantiateController($class);
90+
try {
91+
return parent::instantiateController($class);
92+
} catch (\ArgumentCountError $e) {
93+
}
94+
95+
if ($this->container instanceof Container && in_array($class, $this->container->getRemovedIds(), true)) {
96+
throw new \LogicException(sprintf('Controller "%s" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?', $class), 0, $e);
97+
}
98+
99+
throw $e;
90100
}
91101
}

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

Lines 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/Fragment/AbstractSurrogateFragmentRenderer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ private function generateSignedFragmentUri($uri, Request $request): string
9898
private function containsNonScalars(array $values): bool
9999
{
100100
foreach ($values as $value) {
101-
if (is_array($value) && $this->containsNonScalars($value)) {
102-
return true;
101+
if (is_array($value)) {
102+
return $this->containsNonScalars($value);
103103
} elseif (!is_scalar($value) && null !== $value) {
104104
return true;
105105
}

src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use Psr\Container\ContainerInterface;
1515
use Psr\Log\LoggerInterface;
16+
use Symfony\Component\Debug\ErrorHandler;
17+
use Symfony\Component\DependencyInjection\Container;
1618
use Symfony\Component\HttpFoundation\Request;
1719
use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver;
1820

@@ -106,6 +108,38 @@ public function testNonInstantiableController()
106108
$this->assertSame(array(NonInstantiableController::class, 'action'), $controller);
107109
}
108110

111+
/**
112+
* @expectedException \LogicException
113+
* @expectedExceptionMessage Controller "Symfony\Component\HttpKernel\Tests\Controller\ImpossibleConstructController" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?
114+
*/
115+
public function testNonConstructController()
116+
{
117+
$container = $this->getMockBuilder(Container::class)->getMock();
118+
$container->expects($this->at(0))
119+
->method('has')
120+
->with(ImpossibleConstructController::class)
121+
->will($this->returnValue(true))
122+
;
123+
124+
$container->expects($this->at(1))
125+
->method('has')
126+
->with(ImpossibleConstructController::class)
127+
->will($this->returnValue(false))
128+
;
129+
130+
$container->expects($this->atLeastOnce())
131+
->method('getRemovedIds')
132+
->with()
133+
->will($this->returnValue(array(ImpossibleConstructController::class)))
134+
;
135+
136+
$resolver = $this->createControllerResolver(null, $container);
137+
$request = Request::create('/');
138+
$request->attributes->set('_controller', array(ImpossibleConstructController::class, 'action'));
139+
140+
$resolver->getController($request);
141+
}
142+
109143
public function testNonInstantiableControllerWithCorrespondingService()
110144
{
111145
$service = new \stdClass();
@@ -196,3 +230,14 @@ public static function action()
196230
{
197231
}
198232
}
233+
234+
class ImpossibleConstructController
235+
{
236+
public function __construct($toto, $controller)
237+
{
238+
}
239+
240+
public function action()
241+
{
242+
}
243+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Symfony\Component\HttpKernel\Tests\Event;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
7+
use PHPUnit\Framework\TestCase;
8+
use Symfony\Component\HttpKernel\Tests\TestHttpKernel;
9+
10+
class FilterControllerArgumentsEventTest extends TestCase
11+
{
12+
public function testFilterControllerArgumentsEvent()
13+
{
14+
$filterController = new FilterControllerArgumentsEvent(new TestHttpKernel(), function () {}, array('test'), new Request(), 1);
15+
$this->assertEquals($filterController->getArguments(), array('test'));
16+
}
17+
}

0 commit comments

Comments
 (0)