Skip to content

Commit f93dda3

Browse files
Merge branch '6.4' into 7.3
* 6.4: [HttpKernel] Make `#[Cache]` respect all explicit cache directives set in controller
2 parents f4d7834 + 8f9461b commit f93dda3

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

EventListener/CacheAttributeListener.php

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

1414
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1515
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
16+
use Symfony\Component\HttpFoundation\HeaderBag;
1617
use Symfony\Component\HttpFoundation\Request;
1718
use Symfony\Component\HttpFoundation\Response;
1819
use Symfony\Component\HttpKernel\Attribute\Cache;
@@ -122,29 +123,40 @@ public function onKernelResponse(ResponseEvent $event): void
122123
// Check if the response has a Vary header that should be considered, ignoring cases where
123124
// it's only 'Accept-Language' and the request has the '_vary_by_language' attribute
124125
$hasVary = ['Accept-Language'] === $response->getVary() ? !$request->attributes->get('_vary_by_language') : $response->hasVary();
126+
//Check if cache-control directive was set manually in cacheControl (not auto computed)
127+
$hasCacheControlDirective = new class($response->headers) extends HeaderBag {
128+
public function __construct(private parent $headerBag)
129+
{
130+
}
131+
132+
public function __invoke(string $key): bool
133+
{
134+
return \array_key_exists($key, $this->headerBag->cacheControl);
135+
}
136+
};
125137

126138
foreach (array_reverse($attributes) as $cache) {
127-
if (null !== $cache->smaxage && !$response->headers->hasCacheControlDirective('s-maxage')) {
139+
if (null !== $cache->smaxage && !$hasCacheControlDirective('s-maxage')) {
128140
$response->setSharedMaxAge($this->toSeconds($cache->smaxage));
129141
}
130142

131143
if ($cache->mustRevalidate) {
132144
$response->headers->addCacheControlDirective('must-revalidate');
133145
}
134146

135-
if (null !== $cache->maxage && !$response->headers->hasCacheControlDirective('max-age')) {
147+
if (null !== $cache->maxage && !$hasCacheControlDirective('max-age')) {
136148
$response->setMaxAge($this->toSeconds($cache->maxage));
137149
}
138150

139-
if (null !== $cache->maxStale && !$response->headers->hasCacheControlDirective('max-stale')) {
151+
if (null !== $cache->maxStale && !$hasCacheControlDirective('max-stale')) {
140152
$response->headers->addCacheControlDirective('max-stale', $this->toSeconds($cache->maxStale));
141153
}
142154

143-
if (null !== $cache->staleWhileRevalidate && !$response->headers->hasCacheControlDirective('stale-while-revalidate')) {
155+
if (null !== $cache->staleWhileRevalidate && !$hasCacheControlDirective('stale-while-revalidate')) {
144156
$response->headers->addCacheControlDirective('stale-while-revalidate', $this->toSeconds($cache->staleWhileRevalidate));
145157
}
146158

147-
if (null !== $cache->staleIfError && !$response->headers->hasCacheControlDirective('stale-if-error')) {
159+
if (null !== $cache->staleIfError && !$hasCacheControlDirective('stale-if-error')) {
148160
$response->headers->addCacheControlDirective('stale-if-error', $this->toSeconds($cache->staleIfError));
149161
}
150162

@@ -157,12 +169,14 @@ public function onKernelResponse(ResponseEvent $event): void
157169
}
158170
}
159171

172+
$hasPublicOrPrivateCacheControlDirective = $hasCacheControlDirective('public') || $hasCacheControlDirective('private');
173+
160174
foreach ($attributes as $cache) {
161-
if (true === $cache->public) {
175+
if (true === $cache->public && !$hasPublicOrPrivateCacheControlDirective) {
162176
$response->setPublic();
163177
}
164178

165-
if (false === $cache->public) {
179+
if (false === $cache->public && !$hasPublicOrPrivateCacheControlDirective) {
166180
$response->setPrivate();
167181
}
168182

Tests/EventListener/CacheAttributeListenerTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,30 @@ public function testHasRelevantVaryHeaderBehavior(array $responseVary, array $ca
383383
$this->assertSame($expectedVary, $response->getVary());
384384
}
385385

386+
public function testAttributeRespectsExplicitPrivateFromController()
387+
{
388+
$request = $this->createRequest(new Cache(public: true));
389+
$response = new Response();
390+
$response->setPrivate();
391+
392+
$this->listener->onKernelResponse($this->createEventMock($request, $response));
393+
394+
$this->assertTrue($response->headers->hasCacheControlDirective('private'));
395+
$this->assertFalse($response->headers->hasCacheControlDirective('public'));
396+
}
397+
398+
public function testAttributeRespectsExplicitPublicFromController()
399+
{
400+
$request = $this->createRequest(new Cache(public: false));
401+
$response = new Response();
402+
$response->setPublic();
403+
404+
$this->listener->onKernelResponse($this->createEventMock($request, $response));
405+
406+
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
407+
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
408+
}
409+
386410
public static function provideVaryHeaderScenarios(): \Traversable
387411
{
388412
yield 'no vary headers at all' => [

0 commit comments

Comments
 (0)