Skip to content

Commit 7d45767

Browse files
committed
bug symfony#48509 [HttpKernel] Fix using entities with the #[Cache()] attribute (HypeMC)
This PR was merged into the 6.2 branch. Discussion ---------- [HttpKernel] Fix using entities with the `#[Cache()]` attribute | Q | A | ------------- | --- | Branch? | 6.2 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix symfony#48506 | License | MIT | Doc PR | - The `#[Cache()]` attribute is suppose to be a replacement for the SensioFrameworkExtraBundle ``@Cache`` annotation, but unlike the annotation, it doesn't work when entities are used in expressions. The reason for it is that the `CacheAttributeListener` is triggered by a `kernel.controller` event, while the `EntityValueResolver` is called afterwards: https://github.com/symfony/symfony/blob/97f0ce875d87ef566bf533e34fc8419e36f84e6f/src/Symfony/Component/HttpKernel/HttpKernel.php#L150-L159 This PR attempts to solve this problem by changing the event of the `CacheAttributeListener` to `kernel.controller_arguments`. Even though this has a BC break the listener was introduced in 6.2 so it might still be early enough to make this change? In any case, I don't have a better idea how to fix this, so I'm open to suggestions. Commits ------- c69057b [HttpKernel] Fix using entities with the #[Cache()] attribute
2 parents 4cb2a86 + c69057b commit 7d45767

File tree

2 files changed

+64
-38
lines changed

2 files changed

+64
-38
lines changed

src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Symfony\Component\HttpFoundation\Request;
1717
use Symfony\Component\HttpFoundation\Response;
1818
use Symfony\Component\HttpKernel\Attribute\Cache;
19-
use Symfony\Component\HttpKernel\Event\ControllerEvent;
19+
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
2020
use Symfony\Component\HttpKernel\Event\ResponseEvent;
2121
use Symfony\Component\HttpKernel\KernelEvents;
2222

@@ -47,7 +47,7 @@ public function __construct(
4747
/**
4848
* Handles HTTP validation headers.
4949
*/
50-
public function onKernelController(ControllerEvent $event)
50+
public function onKernelControllerArguments(ControllerArgumentsEvent $event)
5151
{
5252
$request = $event->getRequest();
5353

@@ -63,12 +63,12 @@ public function onKernelController(ControllerEvent $event)
6363
/** @var Cache[] $attributes */
6464
foreach ($attributes as $cache) {
6565
if (null !== $cache->lastModified) {
66-
$lastModified = $this->getExpressionLanguage()->evaluate($cache->lastModified, $request->attributes->all());
66+
$lastModified = $this->getExpressionLanguage()->evaluate($cache->lastModified, array_merge($request->attributes->all(), $event->getNamedArguments()));
6767
($response ??= new Response())->setLastModified($lastModified);
6868
}
6969

7070
if (null !== $cache->etag) {
71-
$etag = hash('sha256', $this->getExpressionLanguage()->evaluate($cache->etag, $request->attributes->all()));
71+
$etag = hash('sha256', $this->getExpressionLanguage()->evaluate($cache->etag, array_merge($request->attributes->all(), $event->getNamedArguments())));
7272
($response ??= new Response())->setEtag($etag);
7373
}
7474
}
@@ -169,7 +169,7 @@ public function onKernelResponse(ResponseEvent $event)
169169
public static function getSubscribedEvents(): array
170170
{
171171
return [
172-
KernelEvents::CONTROLLER => ['onKernelController', 10],
172+
KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelControllerArguments', 10],
173173
KernelEvents::RESPONSE => ['onKernelResponse', -10],
174174
];
175175
}

src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Symfony\Component\HttpFoundation\Request;
1717
use Symfony\Component\HttpFoundation\Response;
1818
use Symfony\Component\HttpKernel\Attribute\Cache;
19-
use Symfony\Component\HttpKernel\Event\ControllerEvent;
19+
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
2020
use Symfony\Component\HttpKernel\Event\ResponseEvent;
2121
use Symfony\Component\HttpKernel\EventListener\CacheAttributeListener;
2222
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -170,35 +170,48 @@ public function testCacheMaxAgeSupportsStrtotimeFormat()
170170
$this->assertSame('86400', $this->response->headers->getCacheControlDirective('stale-if-error'));
171171
}
172172

173-
public function testLastModifiedNotModifiedResponse()
173+
/**
174+
* @testWith ["test.getDate()"]
175+
* ["date"]
176+
*/
177+
public function testLastModifiedNotModifiedResponse(string $expression)
174178
{
175-
$request = $this->createRequest(new Cache(lastModified: 'test.getDate()'));
176-
$request->attributes->set('test', new TestEntity());
179+
$entity = new TestEntity();
180+
181+
$request = $this->createRequest(new Cache(lastModified: $expression));
182+
$request->attributes->set('date', new \DateTimeImmutable('Fri, 23 Aug 2013 00:00:00 GMT'));
177183
$request->headers->add(['If-Modified-Since' => 'Fri, 23 Aug 2013 00:00:00 GMT']);
178184

179185
$listener = new CacheAttributeListener();
180-
$controllerEvent = new ControllerEvent($this->getKernel(), function () {
186+
$controllerArgumentsEvent = new ControllerArgumentsEvent($this->getKernel(), function (TestEntity $test) {
181187
return new Response();
182-
}, $request, null);
188+
}, [$entity], $request, null);
183189

184-
$listener->onKernelController($controllerEvent);
185-
$response = \call_user_func($controllerEvent->getController());
190+
$listener->onKernelControllerArguments($controllerArgumentsEvent);
191+
$response = $controllerArgumentsEvent->getController()($entity);
186192

187193
$this->assertSame(304, $response->getStatusCode());
188194
}
189195

190-
public function testLastModifiedHeader()
196+
/**
197+
* @testWith ["test.getDate()"]
198+
* ["date"]
199+
*/
200+
public function testLastModifiedHeader(string $expression)
191201
{
192-
$request = $this->createRequest(new Cache(lastModified: 'test.getDate()'));
193-
$request->attributes->set('test', new TestEntity());
202+
$entity = new TestEntity();
203+
204+
$request = $this->createRequest(new Cache(lastModified: $expression));
205+
$request->attributes->set('date', new \DateTimeImmutable('Fri, 23 Aug 2013 00:00:00 GMT'));
194206

195207
$listener = new CacheAttributeListener();
196-
$controllerEvent = new ControllerEvent($this->getKernel(), function () {
208+
$controllerArgumentsEvent = new ControllerArgumentsEvent($this->getKernel(), function (TestEntity $test) {
197209
return new Response();
198-
}, $request, null);
199-
$listener->onKernelController($controllerEvent);
210+
}, [$entity], $request, null);
211+
$listener->onKernelControllerArguments($controllerArgumentsEvent);
200212

201-
$responseEvent = new ResponseEvent($this->getKernel(), $request, HttpKernelInterface::MAIN_REQUEST, \call_user_func($controllerEvent->getController()));
213+
$controllerResponse = $controllerArgumentsEvent->getController()($entity);
214+
$responseEvent = new ResponseEvent($this->getKernel(), $request, HttpKernelInterface::MAIN_REQUEST, $controllerResponse);
202215
$listener->onKernelResponse($responseEvent);
203216

204217
$response = $responseEvent->getResponse();
@@ -208,35 +221,48 @@ public function testLastModifiedHeader()
208221
$this->assertSame('Fri, 23 Aug 2013 00:00:00 GMT', $response->headers->get('Last-Modified'));
209222
}
210223

211-
public function testEtagNotModifiedResponse()
224+
/**
225+
* @testWith ["test.getId()"]
226+
* ["id"]
227+
*/
228+
public function testEtagNotModifiedResponse(string $expression)
212229
{
213-
$request = $this->createRequest(new Cache(etag: 'test.getId()'));
214-
$request->attributes->set('test', $entity = new TestEntity());
230+
$entity = new TestEntity();
231+
232+
$request = $this->createRequest(new Cache(etag: $expression));
233+
$request->attributes->set('id', '12345');
215234
$request->headers->add(['If-None-Match' => sprintf('"%s"', hash('sha256', $entity->getId()))]);
216235

217236
$listener = new CacheAttributeListener();
218-
$controllerEvent = new ControllerEvent($this->getKernel(), function () {
237+
$controllerArgumentsEvent = new ControllerArgumentsEvent($this->getKernel(), function (TestEntity $test) {
219238
return new Response();
220-
}, $request, null);
239+
}, [$entity], $request, null);
221240

222-
$listener->onKernelController($controllerEvent);
223-
$response = \call_user_func($controllerEvent->getController());
241+
$listener->onKernelControllerArguments($controllerArgumentsEvent);
242+
$response = $controllerArgumentsEvent->getController()($entity);
224243

225244
$this->assertSame(304, $response->getStatusCode());
226245
}
227246

228-
public function testEtagHeader()
247+
/**
248+
* @testWith ["test.getId()"]
249+
* ["id"]
250+
*/
251+
public function testEtagHeader(string $expression)
229252
{
230-
$request = $this->createRequest(new Cache(etag: 'test.getId()'));
231-
$request->attributes->set('test', $entity = new TestEntity());
253+
$entity = new TestEntity();
254+
255+
$request = $this->createRequest(new Cache(etag: $expression));
256+
$request->attributes->set('id', '12345');
232257

233258
$listener = new CacheAttributeListener();
234-
$controllerEvent = new ControllerEvent($this->getKernel(), function () {
259+
$controllerArgumentsEvent = new ControllerArgumentsEvent($this->getKernel(), function (TestEntity $test) {
235260
return new Response();
236-
}, $request, null);
237-
$listener->onKernelController($controllerEvent);
261+
}, [$entity], $request, null);
262+
$listener->onKernelControllerArguments($controllerArgumentsEvent);
238263

239-
$responseEvent = new ResponseEvent($this->getKernel(), $request, HttpKernelInterface::MAIN_REQUEST, \call_user_func($controllerEvent->getController()));
264+
$controllerResponse = $controllerArgumentsEvent->getController()($entity);
265+
$responseEvent = new ResponseEvent($this->getKernel(), $request, HttpKernelInterface::MAIN_REQUEST, $controllerResponse);
240266
$listener->onKernelResponse($responseEvent);
241267

242268
$response = $responseEvent->getResponse();
@@ -280,8 +306,8 @@ public function testConfigurationDoesNotOverrideAlreadySetResponseHeaders()
280306
public function testAttribute()
281307
{
282308
$request = new Request();
283-
$event = new ControllerEvent($this->getKernel(), [new CacheAttributeController(), 'foo'], $request, null);
284-
$this->listener->onKernelController($event);
309+
$event = new ControllerArgumentsEvent($this->getKernel(), [new CacheAttributeController(), 'foo'], [], $request, null);
310+
$this->listener->onKernelControllerArguments($event);
285311

286312
$response = new Response();
287313
$event = new ResponseEvent($this->getKernel(), $request, HttpKernelInterface::MAIN_REQUEST, $response);
@@ -290,8 +316,8 @@ public function testAttribute()
290316
$this->assertSame(CacheAttributeController::METHOD_SMAXAGE, $response->getMaxAge());
291317

292318
$request = new Request();
293-
$event = new ControllerEvent($this->getKernel(), [new CacheAttributeController(), 'bar'], $request, null);
294-
$this->listener->onKernelController($event);
319+
$event = new ControllerArgumentsEvent($this->getKernel(), [new CacheAttributeController(), 'bar'], [], $request, null);
320+
$this->listener->onKernelControllerArguments($event);
295321

296322
$response = new Response();
297323
$event = new ResponseEvent($this->getKernel(), $request, HttpKernelInterface::MAIN_REQUEST, $response);

0 commit comments

Comments
 (0)