Skip to content

Commit 9150062

Browse files
authored
Merge pull request #541 from FriendsOfSymfony/header-parser
add tag header parser to cleanly respect custom glue
2 parents ec878d6 + f307e8e commit 9150062

8 files changed

+107
-9
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ Changelog
33

44
See also the [GitHub releases page](https://github.com/FriendsOfSymfony/FOSHttpCache/releases).
55

6+
2.15.0
7+
------
8+
9+
* Provide a `TagHeaderParser` that can split up a tag header into the list of tags.
10+
This allows to correctly handle non-default tag separators in all places.
11+
612
2.14.2
713
------
814

doc/symfony-cache-configuration.rst

+5-2
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,7 @@ you have the same configuration options as with the ``PurgeListener``. *Only
208208
set one of ``client_ips`` or ``client_matcher``*. Additionally, you can
209209
configure the HTTP method and header used for tag purging:
210210

211-
* **client_ips**: String with IP or array of IPs that are allowed to
212-
purge the cache.
211+
* **client_ips**: String with IP or array of IPs that are allowed to purge the cache.
213212

214213
**default**: ``127.0.0.1``
215214

@@ -230,6 +229,10 @@ configure the HTTP method and header used for tag purging:
230229

231230
**default**: ``/``
232231

232+
* **tags_parser**: Overwrite if you use a non-default glue to combine the tags in the header.
233+
This option expects a `FOS\HttpCache\TagHeaderFormatter\TagHeaderParser` instance, configured
234+
with the glue you want to use.
235+
233236
To get cache tagging support, register the ``PurgeTagsListener`` and use the
234237
``Psr6Store`` in your ``AppCache``::
235238

src/ResponseTagger.php

+19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use FOS\HttpCache\Exception\InvalidTagException;
1515
use FOS\HttpCache\TagHeaderFormatter\CommaSeparatedTagHeaderFormatter;
1616
use FOS\HttpCache\TagHeaderFormatter\TagHeaderFormatter;
17+
use FOS\HttpCache\TagHeaderFormatter\TagHeaderParser;
1718
use Psr\Http\Message\ResponseInterface;
1819
use Symfony\Component\OptionsResolver\Options;
1920
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -96,6 +97,24 @@ public function getTagsHeaderValue()
9697
return $this->headerFormatter->getTagsHeaderValue($this->tags);
9798
}
9899

100+
/**
101+
* Split the tag header into a list of tags.
102+
*
103+
* @param string|string[] $headers
104+
*
105+
* @return string[]
106+
*/
107+
protected function parseTagsHeaderValue($headers): array
108+
{
109+
if ($this->headerFormatter instanceof TagHeaderParser) {
110+
return $this->headerFormatter->parseTagsHeaderValue($headers);
111+
}
112+
113+
return array_merge(...array_map(function ($header) {
114+
return explode(',', $header);
115+
}, $headers));
116+
}
117+
99118
/**
100119
* Check whether the tag handler has any tags to set on the response.
101120
*

src/SymfonyCache/PurgeTagsListener.php

+11-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace FOS\HttpCache\SymfonyCache;
1313

14+
use FOS\HttpCache\TagHeaderFormatter\CommaSeparatedTagHeaderFormatter;
15+
use FOS\HttpCache\TagHeaderFormatter\TagHeaderParser;
1416
use Symfony\Component\HttpFoundation\Response;
1517
use Symfony\Component\OptionsResolver\OptionsResolver;
1618
use Toflar\Psr6HttpCacheStore\Psr6StoreInterface;
@@ -42,6 +44,11 @@ class PurgeTagsListener extends AccessControlledListener
4244
*/
4345
private $tagsHeader;
4446

47+
/**
48+
* @var TagHeaderParser
49+
*/
50+
private $tagsParser;
51+
4552
/**
4653
* When creating the purge listener, you can configure an additional option.
4754
*
@@ -65,6 +72,7 @@ public function __construct(array $options = [])
6572

6673
$this->tagsMethod = $options['tags_method'];
6774
$this->tagsHeader = $options['tags_header'];
75+
$this->tagsParser = $options['tags_parser'];
6876
}
6977

7078
/**
@@ -125,11 +133,7 @@ public function handlePurgeTags(CacheEvent $event)
125133
$headers = $request->headers->get($this->tagsHeader, '', false);
126134
}
127135

128-
foreach ($headers as $header) {
129-
foreach (explode(',', $header) as $tag) {
130-
$tags[] = $tag;
131-
}
132-
}
136+
$tags = $this->tagsParser->parseTagsHeaderValue($headers);
133137

134138
if ($store->invalidateTags($tags)) {
135139
$response->setStatusCode(200, 'Purged');
@@ -151,9 +155,11 @@ protected function getOptionsResolver()
151155
$resolver->setDefaults([
152156
'tags_method' => static::DEFAULT_TAGS_METHOD,
153157
'tags_header' => static::DEFAULT_TAGS_HEADER,
158+
'tags_parser' => new CommaSeparatedTagHeaderFormatter(),
154159
]);
155160
$resolver->setAllowedTypes('tags_method', 'string');
156161
$resolver->setAllowedTypes('tags_header', 'string');
162+
$resolver->setAllowedTypes('tags_parser', TagHeaderParser::class);
157163

158164
return $resolver;
159165
}

src/TagHeaderFormatter/CommaSeparatedTagHeaderFormatter.php

+12-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @author Yanick Witschi <[email protected]>
1818
*/
19-
class CommaSeparatedTagHeaderFormatter implements TagHeaderFormatter
19+
class CommaSeparatedTagHeaderFormatter implements TagHeaderFormatter, TagHeaderParser
2020
{
2121
/**
2222
* @var string
@@ -53,4 +53,15 @@ public function getTagsHeaderValue(array $tags)
5353
{
5454
return implode($this->glue, $tags);
5555
}
56+
57+
public function parseTagsHeaderValue($tags): array
58+
{
59+
if (is_string($tags)) {
60+
$tags = [$tags];
61+
}
62+
63+
return array_merge(...array_map(function ($tagsFragment) {
64+
return explode($this->glue, $tagsFragment);
65+
}, $tags));
66+
}
5667
}

src/TagHeaderFormatter/MaxHeaderValueLengthFormatter.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*
2222
* @author Yanick Witschi <[email protected]>
2323
*/
24-
class MaxHeaderValueLengthFormatter implements TagHeaderFormatter
24+
class MaxHeaderValueLengthFormatter implements TagHeaderFormatter, TagHeaderParser
2525
{
2626
/**
2727
* @var TagHeaderFormatter
@@ -83,6 +83,15 @@ public function getTagsHeaderValue(array $tags)
8383
return $newValues;
8484
}
8585

86+
public function parseTagsHeaderValue($tags): array
87+
{
88+
if ($this->inner instanceof TagHeaderParser) {
89+
return $this->inner->parseTagsHeaderValue($tags);
90+
}
91+
92+
throw new \BadMethodCallException('The inner formatter does not implement '.TagHeaderParser::class);
93+
}
94+
8695
/**
8796
* @param string $value
8897
*
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache 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\HttpCache\TagHeaderFormatter;
13+
14+
/**
15+
* The TagHeaderParser can convert the tag header into an array of tags.
16+
*
17+
* @author David Buchmann <[email protected]>
18+
*/
19+
interface TagHeaderParser
20+
{
21+
/**
22+
* Split the tag header into a list of tags.
23+
*
24+
* @param string|string[] $tags
25+
*
26+
* @return string[]
27+
*/
28+
public function parseTagsHeaderValue($tags): array;
29+
}

tests/Unit/TagHeaderFormatter/CommaSeparatedTagHeaderFormatterTest.php

+15
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,19 @@ public function testGetCustomGlueTagsHeaderValue()
4646
$this->assertSame('tag1', $formatter->getTagsHeaderValue(['tag1']));
4747
$this->assertSame('tag1 tag2 tag3', $formatter->getTagsHeaderValue(['tag1', 'tag2', 'tag3']));
4848
}
49+
50+
public function testParseTagsHeaderValue()
51+
{
52+
$parser = new CommaSeparatedTagHeaderFormatter();
53+
54+
$this->assertSame(['a', 'b', 'c'], $parser->parseTagsHeaderValue('a,b,c'));
55+
$this->assertSame(['a', 'b', 'c'], $parser->parseTagsHeaderValue(['a', 'b,c']));
56+
}
57+
58+
public function testParseCustomGlueTagsHeaderValue()
59+
{
60+
$parser = new CommaSeparatedTagHeaderFormatter(TagHeaderFormatter::DEFAULT_HEADER_NAME, ' ');
61+
62+
$this->assertSame(['a', 'b,c'], $parser->parseTagsHeaderValue('a b,c'));
63+
}
4964
}

0 commit comments

Comments
 (0)