8000 [DI][Router][DX] Invalidate routing cache when container parameters c… · symfony/symfony@fad4d9e · GitHub
[go: up one dir, main page]

Skip to content

Commit fad4d9e

Browse files
committed
[DI][Router][DX] Invalidate routing cache when container parameters changed
1 parent e58be70 commit fad4d9e

File tree

7 files changed

+261
-0
lines changed

7 files changed

+261
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@
6464
<argument type="collection"></argument>
6565
</service>
6666

67+
<service class="Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker" public="false">
68+
<argument type="service" id="service_container" />
69+
<tag name="config_cache.resource_checker" priority="-980" />
70+
</service>
71+
6772
<service class="Symfony\Component\Config\Resource\SelfCheckingResourceChecker" public="false">
6873
<tag name="config_cache.resource_checker" priority="-990" />
6974
</service>

src/Symfony/Bundle/FrameworkBundle/Routing/Router.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Routing;
1313

14+
use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
1415
use Symfony\Component\Routing\Router as BaseRouter;
1516
use Symfony\Component\Routing\RequestContext;
1617
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -27,6 +28,7 @@
2728
class Router extends BaseRouter implements WarmableInterface
2829
{
2930
private $container;
31+
private $collectedParameters = array();
3032

3133
/**
3234
* Constructor.
@@ -53,6 +55,7 @@ public function getRouteCollection()
5355
if (null === $this->collection) {
5456
$this->collection = $this->container->get('routing.loader')->load($this->resource, $this->options['resource_type']);
5557
$this->resolveParameters($this->collection);
58+
$this->collection->addResource(new ContainerParametersResource($this->collectedParameters));
5659
}
5760

5861
return $this->collection;
@@ -153,6 +156,8 @@ private function resolve($value)
153156
$resolved = $container->getParameter($match[1]);
154157

155158
if (is_string($resolved) || is_numeric($resolved)) {
159+
$this->collectedParameters[$match[1]] = $resolved;
160+
156161
return (string) $resolved;
157162
}
158163

src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php

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

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Bundle\FrameworkBundle\Routing\Router;
16+
use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
1617
use Symfony\Component\Routing\Route;
1718
use Symfony\Component\Routing\RouteCollection;
1819

@@ -217,6 +218,20 @@ public function testDefaultValuesAsNonStrings($value)
217218
$this->assertSame($value, $route->getDefault('foo'));
218219
}
219220

221+
public function testGetRouteCollectionAddsContainerParametersResource()
222+
{
223+
$routeCollection = $this->getMockBuilder(RouteCollection::class)->getMock();
224+
$routeCollection->method('getIterator')->willReturn(new \ArrayIterator(array(new Route('/%locale%'))));
225+
$routeCollection->expects($this->once())->method('addResource')->with(new ContainerParametersResource(array('locale' => 'en')));
226+
227+
$sc = $this->getServiceContainer($routeCollection);
228+
$sc->setParameter('locale', 'en');
229+
230+
$router = new Router($sc, 'foo');
231+
232+
$router->getRouteCollection();
233+
}
234+
220235
public function getNonStringValues()
221236
{
222237
return array(array(null), array(false), array(true), array(new \stdClass()), array(array('foo', 'bar')), array(array(array())));
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\DependencyInjection\Config;
13+
14+
use Symfony\Component\Config\Resource\ResourceInterface;
15+
16+
/**
17+
* Tracks container parameters.
18+
*
19+
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
20+
*/
21+
class ContainerParametersResource implements ResourceInterface, \Serializable
22+
{
23+
private $parameters;
24+
25+
/**
26+
* @param array $parameters The container parameters to track
27+
*/
28+
public function __construct(array $parameters)
29+
{
30+
$this->parameters = $parameters;
31+
}
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
public function __toString()
37+
{
38+
return 'container_parameters_'.md5(serialize($this->parameters));
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function serialize()
45+
{
46+
return serialize($this->parameters);
47+
}
48+
49+
/**
50+
* {@inheritdoc}
51+
*/
52+
public function unserialize($serialized)
53+
{
54+
$this->parameters = unserialize($serialized);
55+
}
56+
57+
/**
58+
* @return array Tracked parameters
59+
*/
60+
public function getParameters()
61+
{
62+
return $this->parameters;
63+
}
64+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\DependencyInjection\Config;
13+
14+
use Symfony\Component\Config\Resource\ResourceInterface;
15+
use Symfony\Component\Config\ResourceCheckerInterface;
16+
use Symfony\Component\DependencyInjection\ContainerInterface;
17+
18+
/**
19+
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
20+
*/
21+
class ContainerParametersResourceChecker implements ResourceCheckerInterface
22+
{
23+
/** @var ContainerInterface */
24+
private $container;
25+
26+
public function __construct(ContainerInterface $container)
27+
{
28+
$this->container = $container;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function supports(ResourceInterface $metadata)
35+
{
36+
return $metadata instanceof ContainerParametersResource;
37+
}
38+
39+
/**
40+
* {@inheritdoc}
41+
*/
42+
public function isFresh(ResourceInterface $resource, $timestamp)
43+
{
44+
foreach ($resource->getParameters() as $key => $value) {
45+
if (!$this->container->hasParameter($key) || $this->container->getParameter($key) !== $value) {
46+
return false;
47+
}
48+
}
49+
50+
return true;
51+
}
52+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
< 1241 /td>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\DependencyInjection\Tests\Config;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Config\ResourceCheckerInterface;
16+
use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
17+
use Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker;
18+
use Symfony\Component\DependencyInjection\ContainerInterface;
19+
20+
class ContainerParametersResourceCheckerTest extends TestCase
21+
{
22+
/** @var ContainerParametersResource */
23+
private $resource;
24+
25+
/** @var ResourceCheckerInterface */
26+
private $resourceChecker;
27+
28+
/** @var ContainerInterface */
29+
private $container;
30+
31+
protected function setUp()
32+
{
33+
$this->resource = new ContainerParametersResource(array('locales' => array('fr', 'en'), 'default_locale' => 'fr'));
34+
$this->container = $this->getMockBuilder(ContainerInterface::class)->getMock();
35+
$this->resourceChecker = new ContainerParametersResourceChecker($this->container);
36+
}
37+
38+
public function testSupports()
39+
{
40+
$this->assertTrue($this->resourceChecker->supports($this->resource));
41+
}
42+
43+
/**
44+
* @dataProvider isFreshProvider
45+
*/
46+
public function testIsFresh(callable $mockContainer, $expected)
47+
{
48+
$mockContainer($this->container);
49+
50+
$this->assertSame($expected, $this->resourceChecker->isFresh($this->resource, time()));
51+
}
52+
53+
public function isFreshProvider()
54+
{
55+
yield 'not fresh on missing parameter' => array(function (\PHPUnit_Framework_MockObject_MockObject $container) {
56+
$container->method('hasParameter')->with('locales')->willReturn(false);
57+
}, false);
58+
59+
yield 'not fresh on different value' => array(function (\PHPUnit_Framework_MockObject_MockObject $container) {
60+
$container->method('getParameter')->with('locales')->willReturn(array('nl', 'es'));
61+
}, false);
62+
63+
yield 'fresh on every identical parameters' => array(function (\PHPUnit_Framework_MockObject_MockObject $container) {
64+
$container->expects($this->exactly(2))->method('hasParameter')->willReturn(true);
65+
$container->expects($this->exactly(2))->method('getParameter')
66+
->withConsecutive(
67+
array($this->equalTo('locales')),
68+
array($this->equalTo('default_locale'))
69+
)
70+
->will($this->returnValueMap(array(
71+
array('locales', array('fr', 'en')),
72+
array('default_locale', 'fr'),
73+
)))
74+
;
75+
}, true);
76+
}
77+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\DependencyInjection\Tests\Config;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
16+
17+
class ContainerParametersResourceTest extends TestCase
18+
{
19+
/** @var ContainerParametersResource */
20+
private $resource;
21+
22+
protected function setUp()
23+
{
24+
$this->resource = new ContainerParametersResource(array('locales' => array('fr', 'en'), 'default_locale' => 'fr'));
25+
}
26+
27+
public function testToString()
28+
{
29+
$this->assertSame('container_parameters_9893d3133814ab03cac3490f36dece77', (string) $this->resource);
30+
}
31+
32+
public function testSerializeUnserialize()
33+
{
34+
$unserialized = unserialize(serialize($this->resource));
35+
36+
$this->assertEquals($this->resource, $unserialized);
37+
}
38+
39+
public function testGetParameters()
40+
{
41+
$this->assertSame(array('locales' => array('fr', 'en'), 'default_locale' => 'fr'), $this->resource->getParameters());
42+
}
43+
}

0 commit comments

Comments
 (0)
0