Skip to content

Commit cf50198

Browse files
committed
minor #16998 [HttpKernel] Add #[Cache()] attribute (javiereguiluz)
This PR was merged into the 6.2 branch. Discussion ---------- [HttpKernel] Add #[Cache()] attribute Fixes #16974. The only article I didn't update was `http_cache/validation.rst` because it uses lots of PHP logic to set cache headers, so I think it cannot be replicated with PHP attributes. Commits ------- 1894141 Add #[Cache()] attribute
2 parents 2a5c011 + 1894141 commit cf50198

File tree

5 files changed

+173
-57
lines changed

5 files changed

+173
-57
lines changed

http_cache.rst

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -191,24 +191,44 @@ Expiration Caching
191191

192192
The *easiest* way to cache a response is by caching it for a specific amount of time::
193193

194-
// src/Controller/BlogController.php
195-
use Symfony\Component\HttpFoundation\Response;
196-
// ...
194+
.. configuration-block::
195+
196+
.. code-block:: php-attributes
197+
198+
// src/Controller/BlogController.php
199+
use Symfony\Component\HttpKernel\Attribute\Cache;
200+
// ...
201+
202+
#[Cache(public: true, maxage: 3600, mustRevalidate: true)]
203+
public function index()
204+
{
205+
return $this->render('blog/index.html.twig', []);
206+
}
207+
208+
.. code-block:: php
197209
198-
public function index()
199-
{
200-
// somehow create a Response object, like by rendering a template
201-
$response = $this->render('blog/index.html.twig', []);
210+
// src/Controller/BlogController.php
211+
use Symfony\Component\HttpFoundation\Response;
212+
// ...
202213
203-
// cache publicly for 3600 seconds
204-
$response->setPublic();
205-
$response->setMaxAge(3600);
214+
public function index()
215+
{
216+
// somehow create a Response object, like by rendering a template
217+
$response = $this->render('blog/index.html.twig', []);
206218
207-
// (optional) set a custom Cache-Control directive
208-
$response->headers->addCacheControlDirective('must-revalidate', true);
219+
// cache publicly for 3600 seconds
220+
$response->setPublic();
221+
$response->setMaxAge(3600);
209222
210-
return $response;
211-
}
223+
// (optional) set a custom Cache-Control directive
224+
$response->headers->addCacheControlDirective('must-revalidate', true);
225+
226+
return $response;
227+
}
228+
229+
.. versionadded:: 6.2
230+
231+
The ``#[Cache()]`` attribute was introduced in Symfony 6.2.
212232

213233
Thanks to this new code, your HTTP response will have the following header:
214234

@@ -316,6 +336,10 @@ Additionally, most cache-related HTTP headers can be set via the single
316336
'etag' => 'abcdef'
317337
]);
318338

339+
.. tip::
340+
341+
All these options are also available when using the ``#[Cache()]`` attribute.
342+
319343
Cache Invalidation
320344
------------------
321345

http_cache/cache_vary.rst

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,28 @@ trigger a different representation of the requested resource:
3232
resource based on the URI and the value of the ``Accept-Encoding`` and
3333
``User-Agent`` request header.
3434

35-
The ``Response`` object offers a clean interface for managing the ``Vary``
36-
header::
35+
Set the ``Vary`` header via the ``Response`` object methods or the ``#[Cache()]``
36+
attribute::
3737

38-
// sets one vary header
39-
$response->setVary('Accept-Encoding');
38+
.. configuration-block::
4039

41-
// sets multiple vary headers
42-
$response->setVary(['Accept-Encoding', 'User-Agent']);
40+
.. code-block:: php-attributes
4341
44-
The ``setVary()`` method takes a header name or an array of header names for
45-
which the response varies.
42+
// this attribute takes an array with the name of the header(s)
43+
// names for which the response varies
44+
use Symfony\Component\HttpKernel\Attribute\Cache;
45+
// ...
46+
47+
#[Cache(vary: ['Accept-Encoding'])]
48+
#[Cache(vary: ['Accept-Encoding', 'User-Agent'])]
49+
public function index()
50+
{
51+
// ...
52+
}
53+
54+
.. code-block:: php
55+
56+
// this method takes a header name or an array of header names for
57+
// which the response varies
58+
$response->setVary('Accept-Encoding');
59+
$response->setVary(['Accept-Encoding', 'User-Agent']);

http_cache/esi.rst

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,20 +166,41 @@ used ``render()``.
166166
The embedded action can now specify its own caching rules entirely independently
167167
of the main page::
168168

169-
// src/Controller/NewsController.php
170-
namespace App\Controller;
169+
.. configuration-block::
171170

172-
// ...
173-
class NewsController extends AbstractController
174-
{
175-
public function latest($maxPerPage)
171+
.. code-block:: php-attributes
172+
173+
// src/Controller/NewsController.php
174+
namespace App\Controller;
175+
176+
use Symfony\Component\HttpKernel\Attribute\Cache;
177+
// ...
178+
179+
class NewsController extends AbstractController
176180
{
177-
// sets to public and adds some expiration
178-
$response->setSharedMaxAge(60);
181+
#[Cache(smaxage: 60)]
182+
public function latest($maxPerPage)
183+
{
184+
// ...
185+
}
186+
}
179187
180-
return $response;
188+
.. code-block:: php
189+
190+
// src/Controller/NewsController.php
191+
namespace App\Controller;
192+
193+
// ...
194+
class NewsController extends AbstractController
195+
{
196+
public function latest($maxPerPage)
197+
{
198+
// sets to public and adds some expiration
199+
$response->setSharedMaxAge(60);
200+
201+
return $response;
202+
}
181203
}
182-
}
183204
184205
In this example, the embedded action is cached publicly too because the contents
185206
are the same for all requests. However, in other cases you may need to make this

http_cache/expiration.rst

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,25 @@ Expiration with the ``Cache-Control`` Header
2424
Most of the time, you will use the ``Cache-Control`` header, which
2525
is used to specify many different cache directives::
2626

27-
// sets the number of seconds after which the response
28-
// should no longer be considered fresh by shared caches
29-
$response->setPublic();
30-
$response->setMaxAge(600);
27+
.. configuration-block::
28+
29+
.. code-block:: php-attributes
30+
31+
use Symfony\Component\HttpKernel\Attribute\Cache;
32+
// ...
33+
34+
#[Cache(public: true, maxage: 600)]
35+
public function index()
36+
{
37+
// ...
38+
}
39+
40+
.. code-block:: php
41+
42+
// sets the number of seconds after which the response
43+
// should no longer be considered fresh by shared caches
44+
$response->setPublic();
45+
$response->setMaxAge(600);
3146
3247
The ``Cache-Control`` header would take on the following format (it may have
3348
additional directives):
@@ -57,13 +72,28 @@ or disadvantage to either.
5772

5873
According to the HTTP specification, "the ``Expires`` header field gives
5974
the date/time after which the response is considered stale." The ``Expires``
60-
header can be set with the ``setExpires()`` ``Response`` method. It takes a
61-
``DateTime`` instance as an argument::
75+
header can be set with the ``expires`` option of the ``#[Cache()]`` attribute or
76+
the ``setExpires()`` ``Response`` method::
77+
78+
.. configuration-block::
79+
80+
.. code-block:: php-attributes
81+
82+
use Symfony\Component\HttpKernel\Attribute\Cache;
83+
// ...
84+
85+
#[Cache(expires: '+600 seconds')]
86+
public function index()
87+
{
88+
// ...
89+
}
90+
91+
.. code-block:: php
6292
63-
$date = new DateTime();
64-
$date->modify('+600 seconds');
93+
$date = new DateTime();
94+
$date->modify('+600 seconds');
6595
66-
$response->setExpires($date);
96+
$response->setExpires($date);
6797
6898
The resulting HTTP header will look like this:
6999

@@ -73,8 +103,8 @@ The resulting HTTP header will look like this:
73103
74104
.. note::
75105

76-
The ``setExpires()`` method automatically converts the date to the GMT
77-
timezone as required by the specification.
106+
The ``expires` option and the ``setExpires()`` method automatically convert
107+
the date to the GMT timezone as required by the specification.
78108

79109
Note that in HTTP versions before 1.1 the origin server wasn't required to
80110
send the ``Date`` header. Consequently, the cache (e.g. the browser) might

http_cache/ssi.rst

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,28 +88,55 @@ Suppose you have a page with private content like a Profile page and you want
8888
to cache a static GDPR content block. With SSI, you can add some expiration
8989
on this block and keep the page private::
9090

91-
// src/Controller/ProfileController.php
92-
namespace App\Controller;
91+
.. configuration-block::
92+
93+
.. code-block:: php-attributes
94+
95+
// src/Controller/ProfileController.php
96+
namespace App\Controller;
97+
98+
use Symfony\Component\HttpKernel\Attribute\Cache;
99+
// ...
93100
94-
// ...
95-
class ProfileController extends AbstractController
96-
{
97-
public function index(): Response
101+
class ProfileController extends AbstractController
98102
{
99-
// by default, responses are private
100-
return $this->render('profile/index.html.twig');
103+
public function index(): Response
104+
{
105+
// by default, responses are private
106+
return $this->render('profile/index.html.twig');
107+
}
108+
109+
#[Cache(smaxage: 600)]
110+
public function gdpr(): Response
111+
{
112+
return $this->render('profile/gdpr.html.twig');
113+
}
101114
}
102115
103-
public function gdpr(): Response
116+
.. code-block:: php
117+
118+
// src/Controller/ProfileController.php
119+
namespace App\Controller;
120+
121+
// ...
122+
class ProfileController extends AbstractController
104123
{
105-
$response = $this->render('profile/gdpr.html.twig');
124+
public function index(): Response
125+
{
126+
// by default, responses are private
127+
return $this->render('profile/index.html.twig');
128+
}
129+
130+
public function gdpr(): Response
131+
{
132+
$response = $this->render('profile/gdpr.html.twig');
106133
107-
// sets to public and adds some expiration
108-
$response->setSharedMaxAge(600);
134+
// sets to public and adds some expiration
135+
$response->setSharedMaxAge(600);
109136
110-
return $response;
137+
return $response;
138+
}
111139
}
112-
}
113140
114141
The profile index page has not public caching, but the GDPR block has
115142
10 minutes of expiration. Let's include this block into the main one:

0 commit comments

Comments
 (0)