Skip to content

Commit 2462398

Browse files
committed
Add global cacheable response config
1 parent 6939757 commit 2462398

22 files changed

+236
-132
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ Changelog
3434

3535
### Rule matcher
3636

37-
* **BC break:** The `match_response` and `additional_cacheable_status` configuration
38-
parameters were removed for individual match rules.
37+
* **BC break:** The `match_response` and `additional_cacheable_status`
38+
configuration parameters were removed for individual match rules.
39+
40+
* Cacheable status codes are now configured globally (`cacheable.response`).
3941

4042
1.3.7
4143
-----

Resources/doc/features/headers.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ This is an example configuration. For more, see the
4141
-
4242
match:
4343
attributes: { _controller: ^AcmeBundle:Default:.* }
44-
additional_cacheable_status: [400]
4544
headers:
4645
cache_control: { public: true, max_age: 15, s_maxage: 30 }
4746
last_modified: "-1 hour"

Resources/doc/reference/configuration.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ for the bundle.
1515
configuration/user-context
1616
configuration/flash-message
1717
configuration/debug
18+
configuration/cacheable
1819
configuration/match
1920
configuration/test
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
cacheable
2+
=========
3+
4+
``response``
5+
------------
6+
7+
Configure which responses are considered :term:`cacheable`. This bundle will
8+
only set Cache-Control headers, including tags etc., on cacheable responses.
9+
10+
``additional_status``
11+
---------------------
12+
13+
**type**: ``array`` **options**: ``auto``, ``true``, ``false``
14+
15+
Following `RFC 7231`_, by default responses are considered cacheable only if
16+
they have status code 200, 203, 204, 206, 300, 301, 404, 405, 410, 414 or 501.
17+
You can add status codes to this list by setting ``additional_status``:
18+
19+
.. code-block:: yaml
20+
21+
# app/config/config.yml
22+
fos_http_cache:
23+
cacheable:
24+
response:
25+
additional_status:
26+
- 100
27+
- 500
28+
29+
``expression``
30+
--------------
31+
32+
**type**: ``string``
33+
34+
An ExpressionLanguage expression to decide whether the response is considered
35+
cacheable. The expression can access the Response object with the response variable.
36+
37+
.. code-block:: yaml
38+
39+
# app/config/config.yml
40+
fos_http_cache:
41+
cacheable:
42+
response:
43+
expression: "response.getStatusCode() >= 300"
44+
45+
You cannot set both match_response and additional_cacheable_status inside the same rule.
46+
47+
.. _RFC 7231: https://tools.ietf.org/html/rfc7231#section-6.1

Resources/doc/reference/configuration/headers.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ cache headers even if they are already set:
3434
-
3535
match:
3636
attributes: { _controller: ^Acme\\TestBundle\\Controller\\DefaultController::.* }
37-
additional_cacheable_status: [400]
3837
headers:
3938
cache_control:
4039
public: true

src/DependencyInjection/Configuration.php

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ function ($v) {
111111
})
112112
;
113113

114+
$this->addCacheableResponseSection($rootNode);
114115
$this->addCacheControlSection($rootNode);
115116
$this->addProxyClientSection($rootNode);
116117
$this->addCacheManagerSection($rootNode);
@@ -124,6 +125,37 @@ function ($v) {
124125
return $treeBuilder;
125126
}
126127

128+
private function addCacheableResponseSection(ArrayNodeDefinition $rootNode)
129+
{
130+
$rootNode
131+
->children()
132+
->arrayNode('cacheable')
133+
->addDefaultsIfNotSet()
134+
->children()
135+
->arrayNode('response')
136+
->addDefaultsIfNotSet()
137+
->children()
138+
->arrayNode('additional_status')
139+
->prototype('scalar')->end()
140+
->info('Additional response HTTP status codes that will be considered cacheable.')
141+
->end()
142+
->scalarNode('expression')
143+
->defaultNull()
144+
->info('Expression to decide whether response is cacheable.')
145+
->end()
146+
->end()
147+
148+
->validate()
149+
->ifTrue(function ($v) {
150+
return !empty($v['additional-status']) && !empty($v['expression']);
151+
})
152+
->thenInvalid('You may not set both additional_status and expression.')
153+
->end()
154+
->end()
155+
->end()
156+
->end();
157+
}
158+
127159
/**
128160
* Cache header control main section.
129161
*
@@ -222,12 +254,6 @@ private function addMatch(NodeBuilder $rules)
222254
->fixXmlConfig('method')
223255
->fixXmlConfig('ip')
224256
->fixXmlConfig('attribute')
225-
->validate()
226-
->ifTrue(function ($v) {
227-
return !empty($v['additional_cacheable_status']) && !empty($v['match_response']);
228-
})
229-
->thenInvalid('You may not set both additional_cacheable_status and match_response.')
230-
->end()
231257
->validate()
232258
->ifTrue(function ($v) {
233259
return !empty($v['match_response']) && !class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage');
@@ -264,14 +290,6 @@ private function addMatch(NodeBuilder $rules)
264290
->prototype('scalar')->end()
265291
->info('Regular expressions on request attributes.')
266292
->end()
267-
->arrayNode('additional_cacheable_status')
268-
->prototype('scalar')->end()
269-
->info('Additional response HTTP status codes that will match.')
270-
->end()
271-
->scalarNode('match_response')
272-
->defaultNull()
273-
->info('Expression to decide whether response should be matched. Replaces HTTP code check and additional_cacheable_status.')
274-
->end()
275293
->end()
276294
->end()
277295
;

src/DependencyInjection/FOSHttpCacheExtension.php

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

1414
use FOS\HttpCache\ProxyClient\HttpDispatcher;
1515
use FOS\HttpCacheBundle\DependencyInjection\Compiler\HashGeneratorPass;
16+
use FOS\HttpCacheBundle\Http\ResponseMatcher\ExpressionResponseMatcher;
1617
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1718
use Symfony\Component\Config\FileLocator;
1819
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -53,6 +54,8 @@ public function load(array $configs, ContainerBuilder $container)
5354
$loader->load('cache_control_listener.xml');
5455
}
5556

57+
$this->loadCacheable($container, $config['cacheable']);
58+
5659
if (!empty($config['cache_control'])) {
5760
$this->loadCacheControl($container, $config['cache_control']);
5861
}
@@ -131,6 +134,22 @@ public function load(array $configs, ContainerBuilder $container)
131134
}
132135
}
133136

137+
private function loadCacheable(ContainerBuilder $container, array $config)
138+
{
139+
$container->setParameter(
140+
$this->getAlias().'.cacheable.response.additional_status',
141+
$config['response']['additional_status']
142+
);
143+
144+
$definition = $container->getDefinition($this->getAlias().'.response_matcher.cacheable');
145+
146+
// Change CacheableResponseMatcher to ExpressionResponseMatcher
147+
if ($config['response']['expression']) {
148+
$definition->setClass(ExpressionResponseMatcher::class)
149+
->setArguments([$config['response']['expression']]);
150+
}
151+
}
152+
134153
/**
135154
* @param ContainerBuilder $container
136155
* @param array $config

src/EventListener/AbstractRuleListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ abstract class AbstractRuleListener
2626
* request and response are matched.
2727
*
2828
* @param RequestMatcherInterface $requestMatcher The headers apply to responses matched by this matcher
29-
* @param array $settings An array of header configuration
29+
* @param array $settings An array of header configuration
3030
*/
3131
public function addRule(
3232
RequestMatcherInterface $requestMatcher,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCacheBundle package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.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 FOS\HttpCacheBundle\Http\ResponseMatcher;
13+
14+
use Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage;
15+
use Symfony\Component\HttpFoundation\Response;
16+
17+
class ExpressionResponseMatcher implements ResponseMatcherInterface
18+
{
19+
private $expressionLanguage;
20+
private $expression;
21+
22+
public function __construct($expression, ExpressionLanguage $expressionLanguage)
23+
{
24+
$this->expression = $expression;
25+
$this->expressionLanguage = $expressionLanguage;
26+
}
27+
28+
public function matches(Response $response)
29+
{
30+
return $this->getExpressionLanguage()->evaluate(
31+
$this->expression,
32+
['response' => $response]
33+
);
34+
}
35+
36+
private function getExpressionLanguage()
37+
{
38+
if (!$this->expressionLanguage) {
39+
$this->expressionLanguage = new ExpressionLanguage();
40+
}
41+
42+
return $this->expressionLanguage;
43+
}
44+
}

src/Resources/config/matcher.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444

4545
<service id="fos_http_cache.response_matcher.cacheable"
4646
class="FOS\HttpCacheBundle\Http\ResponseMatcher\CacheableResponseMatcher"
47-
public="false"/>
47+
public="false">
48+
<argument>%fos_http_cache.cacheable.response.additional_status%</argument>
49+
</service>
4850

4951
<service id="fos_http_cache.response_matcher.non_error"
5052
class="FOS\HttpCacheBundle\Http\ResponseMatcher\NonErrorResponseMatcher"

tests/Functional/Fixtures/app/config/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ framework:
99
storage_id: session.test_storage
1010

1111
fos_http_cache:
12+
cacheable:
13+
response:
14+
additional_status:
15+
- 100
1216
cache_control:
1317
rules:
1418
-
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCacheBundle package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.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+
$container->loadFromExtension('fos_http_cache', [
13+
'cacheable' => [
14+
'response' => [
15+
'expression' => 'response.getStatusCode() >= 300',
16+
],
17+
],
18+
]);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services">
3+
4+
<config xmlns="http://example.org/schema/dic/fos_http_cache">
5+
<cacheable>
6+
<response>
7+
<expression>response.getStatusCode() >= 300</expression>
8+
</response>
9+
</cacheable>
10+
</config>
11+
</container>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fos_http_cache:
2+
cacheable:
3+
response:
4+
expression: 'response.getStatusCode() >= 300'

tests/Resources/Fixtures/config/full.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
*/
1111

1212
$container->loadFromExtension('fos_http_cache', [
13+
'cacheable' => [
14+
'response' => [
15+
'additional_status' => [100, 500],
16+
],
17+
],
1318
'cache_control' => [
1419
'defaults' => [
1520
'overwrite' => true,
@@ -22,7 +27,6 @@
2227
'methods' => ['GET', 'POST'],
2328
'ips' => ['1.2.3.4', '1.1.1.1'],
2429
'attributes' => ['_controller' => 'fos.user_bundle.*'],
25-
'additional_cacheable_status' => [100, 500],
2630
],
2731
'headers' => [
2832
'overwrite' => false,
@@ -72,7 +76,6 @@
7276
'attributes' => [
7377
'_foo' => 'bar',
7478
],
75-
'additional_cacheable_status' => [501, 502],
7679
],
7780
'tags' => ['a', 'b'],
7881
'tag_expressions' => ['"a"', '"b"'],
@@ -92,7 +95,6 @@
9295
'attributes' => [
9396
'_format' => 'json',
9497
],
95-
'additional_cacheable_status' => [404, 403],
9698
],
9799
'routes' => [
98100
'invalidate_route1' => [

tests/Resources/Fixtures/config/full.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
<container xmlns="http://symfony.com/schema/dic/services">
33

44
<config xmlns="http://example.org/schema/dic/fos_http_cache">
5+
<cacheable>
6+
<response>
7+
<additional-status>100</additional-status>
8+
<additional-status>500</additional-status>
9+
</response>
10+
</cacheable>
511
<cache-control>
612
<defaults>
713
<overwrite>true</overwrite>
@@ -16,8 +22,6 @@
1622
<ip>1.2.3.4</ip>
1723
<ip>1.1.1.1</ip>
1824
<attribute name="_controller">fos.user_bundle.*</attribute>
19-
<additional-cacheable-status>100</additional-cacheable-status>
20-
<additional-cacheable-status>500</additional-cacheable-status>
2125
</match>
2226
<headers etag="true" last-modified="-1 hour" reverse-proxy-ttl="42">
2327
<overwrite>false</overwrite>
@@ -54,8 +58,6 @@
5458
<method>DELETE</method>
5559
<ip>99.99.99.99</ip>
5660
<attribute name="_foo">bar</attribute>
57-
<additional-cacheable-status>501</additional-cacheable-status>
58-
<additional-cacheable-status>502</additional-cacheable-status>
5961
</match>
6062
<tag>a</tag>
6163
<tag>b</tag>
@@ -70,8 +72,6 @@
7072
<method>PATCH</method>
7173
<ip>42.42.42.42</ip>
7274
<attribute name="_format">json</attribute>
73-
<additional-cacheable-status>404</additional-cacheable-status>
74-
<additional-cacheable-status>403</additional-cacheable-status>
7575
</match>
7676
<route name="invalidate_route1" ignore-extra-params="false"/>
7777
</rule>

0 commit comments

Comments
 (0)