Skip to content

Commit 691876a

Browse files
committed
Merge pull request #67 from FriendsOfSymfony/configure-tags
Also have configuration rules for tags
2 parents 8d78768 + 27ec9ef commit 691876a

File tree

16 files changed

+431
-174
lines changed

16 files changed

+431
-174
lines changed

DependencyInjection/Configuration.php

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1010

1111
/**
12-
* This is the class that validates and merges configuration from your app/config files
12+
* This class contains the configuration information for the bundle
1313
*
14-
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
14+
* This information is solely responsible for how the different configuration
15+
* sections are normalized, and merged.
16+
*
17+
* @author David de Boer <david@driebit.nl>
18+
* @author David Buchmann <mail@davidbu.ch>
1519
*/
1620
class Configuration implements ConfigurationInterface
1721
{
@@ -24,6 +28,18 @@ public function getConfigTreeBuilder()
2428
$rootNode = $treeBuilder->root('fos_http_cache');
2529

2630
$rootNode
31+
->validate()
32+
->ifTrue(function ($v) {return $v['cache_manager']['enabled'] && !isset($v['proxy_client']);})
33+
->then(function ($v) {
34+
if ('auto' === $v['cache_manager']['enabled']) {
35+
$v['cache_manager']['enabled'] = false;
36+
37+
return $v;
38+
}
39+
throw new InvalidConfigurationException('You need to configure a proxy_client to use the cache_manager.');
40+
})
41+
->end()
42+
2743
->children()
2844
->booleanNode('debug')
2945
->defaultValue('%kernel.debug%')
@@ -39,9 +55,8 @@ public function getConfigTreeBuilder()
3955
$this->addUserContextListenerSection($rootNode);
4056
$this->addRulesSection($rootNode);
4157
$this->addProxyClientSection($rootNode);
42-
$this->addTagListenerSection($rootNode);
58+
$this->addCacheManager($rootNode);
4359
$this->addFlashMessageListenerSection($rootNode);
44-
$this->addInvalidatorsSection($rootNode);
4560

4661
return $treeBuilder;
4762
}
@@ -106,6 +121,21 @@ private function addRulesSection(ArrayNodeDefinition $rootNode)
106121

107122
$this->addMatchSection($rules);
108123
$this->addHeaderSection($rules);
124+
$this->addTagSection($rules);
125+
}
126+
127+
private function addTagSection(NodeBuilder $rules)
128+
{
129+
$rules
130+
->arrayNode('tags')
131+
->prototype('scalar')
132+
->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
133+
->end()->end()
134+
->arrayNode('tag_expressions')
135+
->prototype('scalar')
136+
->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
137+
->end()
138+
;
109139
}
110140

111141
private function addMatchSection(NodeBuilder $rules)
@@ -221,9 +251,37 @@ private function addProxyClientSection(ArrayNodeDefinition $rootNode)
221251
->end();
222252
}
223253

224-
private function addTagListenerSection(ArrayNodeDefinition $rootNode)
254+
private function addCacheManager(ArrayNodeDefinition $rootNode)
225255
{
226-
$rootNode
256+
$invalidationNode = $rootNode
257+
->children()
258+
->arrayNode('cache_manager')
259+
->addDefaultsIfNotSet()
260+
->beforeNormalization()
261+
->ifArray()
262+
->then(function ($v) {
263+
$v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true;
264+
265+
return $v;
266+
})
267+
->end()
268+
->info('Configure the cache manager. Needs a proxy_client to be configured.')
269+
->children()
270+
->enumNode('enabled')
271+
->values(array(true, false, 'auto'))
272+
->defaultValue('auto')
273+
->info('Allows to disable the invalidation manager. Enabled by default if you configure a proxy client.')
274+
->end()
275+
->end()
276+
;
277+
278+
$this->addTagListenerSection($invalidationNode);
279+
$this->addInvalidatorsSection($invalidationNode);
280+
}
281+
282+
private function addTagListenerSection(ArrayNodeDefinition $invalidationNode)
283+
{
284+
$invalidationNode
227285
->children()
228286
->arrayNode('tag_listener')
229287
->addDefaultsIfNotSet()
@@ -272,21 +330,24 @@ private function addFlashMessageListenerSection(ArrayNodeDefinition $rootNode)
272330
->end();
273331
}
274332

275-
private function addInvalidatorsSection(ArrayNodeDefinition $rootNode)
333+
private function addInvalidatorsSection(ArrayNodeDefinition $invalidationNode)
276334
{
277-
$rootNode
335+
$invalidationNode
278336
->children()
279-
->arrayNode('invalidators')
337+
->arrayNode('route_invalidators')
280338
->useAttributeAsKey('name')
339+
->info('Groups of origin routes that invalidate target routes when a request is made')
281340
->prototype('array')
282341
->children()
283342
->arrayNode('origin_routes')
284343
->isRequired()
285344
->requiresAtLeastOneElement()
286345
->prototype('scalar')->end()
346+
->info('Invalidate the target routes in this group when one of these routes is called')
287347
->end()
288348
->arrayNode('invalidate_routes')
289349
->useAttributeAsKey('name')
350+
->info('Target routes to invalidate when an origin route is called')
290351
->prototype('array')
291352
->children()
292353
->scalarNode('parameter_mapper')->end()

DependencyInjection/FOSHttpCacheExtension.php

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,36 +33,16 @@ public function load(array $configs, ContainerBuilder $container)
3333
$loader->load('cache_control_listener.xml');
3434
}
3535

36-
if (!empty($config['rules'])) {
37-
$this->loadRules($container, $config);
38-
}
39-
4036
if (isset($config['proxy_client'])) {
41-
$container->setParameter($this->getAlias().'.invalidators', $config['invalidators']);
4237
$this->loadProxyClient($container, $loader, $config['proxy_client']);
38+
}
4339

44-
$loader->load('cache_manager.xml');
45-
46-
if ($config['tag_listener']['enabled']) {
47-
// true or auto
48-
if (class_exists('\Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
49-
$loader->load('tag_listener.xml');
50-
} elseif (true === $config['tag_listener']['enabled']) {
51-
// silently skip if set to auto
52-
throw new \RuntimeException('The TagListener requires symfony/expression-language');
53-
}
54-
}
40+
if ($config['cache_manager']['enabled'] && isset($config['proxy_client'])) {
41+
$this->loadCacheManager($container, $loader, $config['cache_manager']);
42+
}
5543

56-
if (version_compare(Kernel::VERSION, '2.4.0', '>=')) {
57-
$container
58-
->getDefinition('fos_http_cache.command.invalidate_path')
59-
->addTag('console.command')
60-
;
61-
}
62-
} elseif (!empty($config['invalidators'])) {
63-
throw new InvalidConfigurationException('You need to configure a proxy client to use the invalidators.');
64-
} elseif (true === $config['tag_listener']['enabled']) {
65-
throw new InvalidConfigurationException('You need to configure a proxy client to use the tag listener.');
44+
if (!empty($config['rules'])) {
45+
$this->loadRules($container, $config);
6646
}
6747

6848
if ($config['user_context']['enabled']) {
@@ -78,14 +58,13 @@ public function load(array $configs, ContainerBuilder $container)
7858

7959
/**
8060
* @param ContainerBuilder $container
81-
* @param $config
61+
* @param array $config
62+
*
63+
* @throws InvalidConfigurationException
8264
*/
83-
protected function loadRules(ContainerBuilder $container, $config)
65+
protected function loadRules(ContainerBuilder $container, array $config)
8466
{
8567
foreach ($config['rules'] as $rule) {
86-
if (!isset($rule['headers'])) {
87-
continue;
88-
}
8968
$match = $rule['match'];
9069

9170
$match['ips'] = (empty($match['ips'])) ? null : $match['ips'];
@@ -111,11 +90,27 @@ protected function loadRules(ContainerBuilder $container, $config)
11190
$extraCriteria
11291
);
11392

93+
$tags = array(
94+
'tags' => $rule['tags'],
95+
'expressions' => $rule['tag_expressions'],
96+
);
97+
if (count($tags['tags']) || count($tags['expressions'])) {
98+
if (!$container->hasDefinition($this->getAlias() . '.event_listener.tag')) {
99+
throw new InvalidConfigurationException('To configure tags, you need to have the tag event listener enabled, requiring symfony/expression-language');
100+
}
101+
102+
$container
103+
->getDefinition($this->getAlias() . '.event_listener.tag')
104+
->addMethodCall('addRule', array($ruleMatcher, $tags))
105+
;
106+
}
114107

115-
$container
116-
->getDefinition($this->getAlias() . '.event_listener.cache_control')
117-
->addMethodCall('add', array($ruleMatcher, $rule['headers']))
118-
;
108+
if (isset($rule['headers'])) {
109+
$container
110+
->getDefinition($this->getAlias() . '.event_listener.cache_control')
111+
->addMethodCall('addRule', array($ruleMatcher, $rule['headers']))
112+
;
113+
}
119114
}
120115
}
121116

@@ -209,6 +204,37 @@ protected function loadVarnish(ContainerBuilder $container, XmlFileLoader $loade
209204
$container->setParameter($this->getAlias() . '.proxy_client.varnish.base_url', $config['base_url']);
210205
}
211206

207+
protected function loadCacheManager(ContainerBuilder $container, XmlFileLoader $loader, array $config)
208+
{
209+
$container->setParameter($this->getAlias().'.cache_manager.route_invalidators', $config['route_invalidators']);
210+
211+
$container->setParameter(
212+
$this->getAlias() . '.cache_manager.additional_status',
213+
isset($config['additional_status']) ? $config['additional_status'] : array()
214+
);
215+
$container->setParameter(
216+
$this->getAlias() . '.cache_manager.match_response',
217+
isset($config['match_response']) ? $config['match_response'] : null
218+
);
219+
$loader->load('cache_manager.xml');
220+
if (version_compare(Kernel::VERSION, '2.4.0', '>=')) {
221+
$container
222+
->getDefinition('fos_http_cache.command.invalidate_path')
223+
->addTag('console.command')
224+
;
225+
}
226+
227+
if ($config['tag_listener']['enabled']) {
228+
// true or auto
229+
if (class_exists('\Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
230+
$loader->load('tag_listener.xml');
231+
} elseif (true === $config['tag_listener']['enabled']) {
232+
// silently skip if set to auto
233+
throw new InvalidConfigurationException('The TagListener requires symfony/expression-language');
234+
}
235+
}
236+
}
237+
212238
private function validateUrl($url, $msg)
213239
{
214240
if (false === strpos($url, '://')) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace FOS\HttpCacheBundle\EventListener;
4+
5+
use FOS\HttpCacheBundle\Http\RuleMatcherInterface;
6+
use Symfony\Component\HttpFoundation\Request;
7+
use Symfony\Component\HttpFoundation\Response;
8+
9+
class AbstractRuleSubscriber
10+
{
11+
/**
12+
* @var array List of arrays with RuleMatcher, settings array.
13+
*/
14+
private $rulesMap = array();
15+
16+
/**
17+
* Add a rule matcher with a list of header directives to apply if the
18+
* request and response are matched.
19+
*
20+
* @param RuleMatcherInterface $ruleMatcher The headers apply to responses matched by this matcher.
21+
* @param array $settings An array of header configuration.
22+
* @param int $priority Optional priority of this matcher. Higher priority is applied first.
23+
*/
24+
public function addRule(
25+
RuleMatcherInterface $ruleMatcher,
26+
array $settings = array(),
27+
$priority = 0
28+
) {
29+
if (!isset($this->rulesMap[$priority])) {
30+
$this->rulesMap[$priority] = array();
31+
}
32+
$this->rulesMap[$priority][] = array($ruleMatcher, $settings);
33+
}
34+
/**
35+
* Return the settings for the current request if any rule matches.
36+
*
37+
* @param Request $request
38+
* @param Response $response
39+
*
40+
* @return array|false Settings to apply or false if no rule matched.
41+
*/
42+
protected function matchConfiguration(Request $request, Response $response)
43+
{
44+
foreach ($this->getRules() as $elements) {
45+
if ($elements[0]->matches($request, $response)) {
46+
return $elements[1];
47+
}
48+
}
49+
50+
return false;
51+
}
52+
53+
/**
54+
* Get the rules ordered by priority.
55+
*
56+
* @return array of array with rule matcher, settings
57+
*/
58+
private function getRules()
59+
{
60+
$sortedRules = array();
61+
krsort($this->rulesMap);
62+
foreach ($this->rulesMap as $rules) {
63+
$sortedRules = array_merge($sortedRules, $rules);
64+
}
65+
66+
return $sortedRules;
67+
}
68+
}

0 commit comments

Comments
 (0)