Skip to content

Commit 59a0bcf

Browse files
committed
Merge pull request #171 from FriendsOfSymfony/tag-http-response
addTags on TagHandler
2 parents 3e535bc + b089678 commit 59a0bcf

File tree

6 files changed

+121
-9
lines changed

6 files changed

+121
-9
lines changed

doc/_static/fos.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.versionmodified {
2+
font-weight: bold;
3+
}

doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
def setup(app):
133133
app.add_javascript('tabs.js')
134134
app.add_stylesheet('tabs.css')
135+
app.add_stylesheet('fos.css')
135136

136137
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
137138
# using the given strftime format.

doc/invalidation-handlers.rst

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ to simplify common operations.
99
Tag Handler
1010
-----------
1111

12-
The tag handler helps you to invalidate all cache entries that where marked
13-
with a specified tag. It works only with a ``CacheInvalidator`` that supports
14-
``CacheInvalidator::INVALIDATE``.
12+
.. versionadded:: 1.3
13+
The tag handler was added in FOSHttpCache 1.3. If you are using an older
14+
version of the library and can not update, you need to use
15+
``CacheInvalidator::invalidateTags``.
16+
17+
The tag handler helps you to mark responses with tags that you can later use to
18+
invalidate all cache entries with that tag. Tag invalidation works only with a
19+
``CacheInvalidator`` that supports ``CacheInvalidator::INVALIDATE``.
1520

1621
Setup
1722
~~~~~
@@ -34,8 +39,22 @@ Usage
3439

3540
With tags you can group related representations so it becomes easier to
3641
invalidate them. You will have to make sure your web application adds the
37-
correct tags on all responses by setting the ``X-Cache-Tags`` header. The
38-
FOSHttpCacheBundle_ does this for you when you’re using Symfony.
42+
correct tags on all responses. You can add tags to the handler using::
43+
44+
$tagHandler->addTags(array('tag-two', 'group-a'));
45+
46+
Before any content is sent out, you need to send the tag header_::
47+
48+
header(sprintf('%s: %s'),
49+
$tagHandler->getTagsHeaderName(),
50+
$tagHandler->getTagsHeaderValue()
51+
);
52+
53+
.. tip::
54+
55+
If you are using Symfony with the FOSHttpCacheBundle_, the tag header is
56+
set automatically. You also have `additional methods of defining tags`_ with
57+
annotations and on URL patterns.
3958

4059
Assume you sent four responses:
4160

@@ -74,3 +93,6 @@ header ``X-Cache-Tags`` in the constructor::
7493

7594
Make sure to reflect this change in your
7695
:doc:`caching proxy configuration <proxy-configuration>`.
96+
97+
.. _header: http://php.net/header
98+
.. _additional methods of defining tags: http://foshttpcachebundle.readthedocs.org/en/latest/features/tagging.html

src/CacheInvalidator.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber)
167167
*/
168168
public function setTagsHeader($tagsHeader)
169169
{
170-
if ($this->tagHandler && $this->tagsHeader !== $tagsHeader) {
170+
if ($this->tagHandler && $this->tagHandler->getTagsHeaderName() !== $tagsHeader) {
171171
$this->tagHandler = new TagHandler($this, $tagsHeader);
172172
}
173173

@@ -179,11 +179,11 @@ public function setTagsHeader($tagsHeader)
179179
*
180180
* @return string
181181
*
182-
* @deprecated Use TagHandler::getTagsHeader instead.
182+
* @deprecated Use TagHandler::getTagsHeaderName instead.
183183
*/
184184
public function getTagsHeader()
185185
{
186-
return $this->tagsHeader;
186+
return $this->tagHandler ? $this->tagHandler->getTagsHeaderName() : $this->tagsHeader;
187187
}
188188

189189
/**

src/Handler/TagHandler.php

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class TagHandler
3232
*/
3333
private $tagsHeader;
3434

35+
/**
36+
* @var array
37+
*/
38+
private $tags = array();
39+
3540
/**
3641
* Constructor
3742
*
@@ -49,6 +54,40 @@ public function __construct(CacheInvalidator $invalidator, $tagsHeader = 'X-Cach
4954
$this->tagsHeader = $tagsHeader;
5055
}
5156

57+
/**
58+
* Get the HTTP header name that will hold cache tags.
59+
*
60+
* @return string
61+
*/
62+
public function getTagsHeaderName()
63+
{
64+
return $this->tagsHeader;
65+
}
66+
67+
/**
68+
* Get the value for the HTTP tag header.
69+
*
70+
* This concatenates all tags and ensures correct encoding.
71+
*
72+
* @return string
73+
*/
74+
public function getTagsHeaderValue()
75+
{
76+
return implode(',', array_unique($this->escapeTags($this->tags)));
77+
}
78+
79+
/**
80+
* Add tags to be sent.
81+
*
82+
* This must be called before any response is sent to the client.
83+
*
84+
* @param array $tags List of tags to add.
85+
*/
86+
public function addTags(array $tags)
87+
{
88+
$this->tags = array_merge($this->tags, $tags);
89+
}
90+
5291
/**
5392
* Invalidate cache entries that contain any of the specified tags in their
5493
* tag header.
@@ -59,10 +98,26 @@ public function __construct(CacheInvalidator $invalidator, $tagsHeader = 'X-Cach
5998
*/
6099
public function invalidateTags(array $tags)
61100
{
62-
$tagExpression = sprintf('(%s)(,.+)?$', implode('|', array_map('preg_quote', $tags)));
101+
$tagExpression = sprintf('(%s)(,.+)?$', implode('|', array_map('preg_quote', $this->escapeTags($tags))));
63102
$headers = array($this->tagsHeader => $tagExpression);
64103
$this->invalidator->invalidate($headers);
65104

66105
return $this;
67106
}
107+
108+
/**
109+
* Make sure that the tags are valid.
110+
*
111+
* @param array $tags The tags to escape.
112+
*
113+
* @return array Sane tags.
114+
*/
115+
protected function escapeTags(array $tags)
116+
{
117+
array_walk($tags, function (&$tag) {
118+
$tag = str_replace(array(',', "\n"), array('_', '_'), $tag);
119+
});
120+
121+
return $tags;
122+
}
68123
}

tests/Unit/Handler/TagHandlerTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,26 @@ public function testInvalidateTagsCustomHeader()
4545
->getMock();
4646

4747
$tagHandler = new TagHandler($cacheInvalidator, 'Custom-Tags');
48+
$this->assertEquals('Custom-Tags', $tagHandler->getTagsHeaderName());
4849
$tagHandler->invalidateTags(array('post-1'));
4950
}
5051

52+
public function testEscapingTags()
53+
{
54+
$cacheInvalidator = \Mockery::mock('FOS\HttpCache\CacheInvalidator')
55+
->shouldReceive('invalidate')
56+
->with(array('X-Cache-Tags' => '(post_test)(,.+)?$'))
57+
->once()
58+
->shouldReceive('supports')
59+
->with(CacheInvalidator::INVALIDATE)
60+
->once()
61+
->andReturn(true)
62+
->getMock();
63+
64+
$tagHandler = new TagHandler($cacheInvalidator);
65+
$tagHandler->invalidateTags(array('post,test'));
66+
}
67+
5168
/**
5269
* @expectedException \FOS\HttpCache\Exception\UnsupportedProxyOperationException
5370
*/
@@ -62,4 +79,18 @@ public function testInvalidateUnsupported()
6279

6380
new TagHandler($cacheInvalidator);
6481
}
82+
83+
public function testTagResponse()
84+
{
85+
$cacheInvalidator = \Mockery::mock('FOS\HttpCache\CacheInvalidator')
86+
->shouldReceive('supports')
87+
->with(CacheInvalidator::INVALIDATE)
88+
->once()
89+
->andReturn(true)
90+
->getMock();
91+
92+
$tagHandler = new TagHandler($cacheInvalidator);
93+
$tagHandler->addTags(array('post-1', 'test,post'));
94+
$this->assertEquals('post-1,test_post', $tagHandler->getTagsHeaderValue());
95+
}
6596
}

0 commit comments

Comments
 (0)