8000 feature #21767 [DI][Router][DX] Invalidate routing cache when contain… · symfony/symfony@0db9723 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0db9723

Browse files
committed
feature #21767 [DI][Router][DX] Invalidate routing cache when container parameters changed (ogizanagi)
This PR was merged into the 3.3-dev branch. Discussion ---------- [DI][Router][DX] Invalidate routing cache when container parameters changed | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #21426 | License | MIT | Doc PR | N/A Supersedes #21443 but only for master. Indeed, this implementation uses a new feature: a `ContainerParametersResource` which compares cached containers parameters (collected at some point, here by the `Router`) with current ones in the container. On the contrary of the previous PR targeting 2.7, this will only invalidate routing cache when parameters actually used in the routes changed and will avoid always rebuilding the routing cache when the container is rebuilt, just to catch the edge case of someone modifying a parameter. Commits ------- fad4d9e [DI][Router][DX] Invalidate routing cache when container parameters changed
2 parents 2d4a9cd + fad4d9e commit 0db9723

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
@@ -60,6 +60,11 @@
6060
<argument /> <!-- resource checkers -->
6161
</service>
6262

63+
<service class="Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker" public="false">
64+
<argument type="service" id="service_container" />
65+
<tag name="config_cache.resource_checker" priority="-980" />
66+
</service>
67+
6368
<service class="Symfony\Component\Config\Resource\SelfCheckingResourceChecker" public="false">
6469
<tag name="config_cache.resource_checker" priority="-990" />
6570
</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+
*
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