Skip to content

Commit a9ab663

Browse files
fix(symfony): ensure ErrorListener is fully stateless to prevent state contamination in Worker mode
1 parent fcfaf38 commit a9ab663

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/Symfony/EventListener/ErrorListener.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,19 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re
8080

8181
// Let the error handler take this we don't handle HTML nor non-api platform requests
8282
if (false === ($apiOperation?->getExtraProperties()['_api_error_handler'] ?? true) || 'html' === $format) {
83-
$this->controller = 'error_controller';
83+
$dup = parent::duplicateRequest($exception, $request);
84+
// Override parent's `_controller` attribute without mutating $this->controller (worker-mode safety).
85+
$dup->attributes->set('_controller', 'error_controller');
8486

85-
return parent::duplicateRequest($exception, $request);
87+
return $dup;
8688
}
8789

8890
if ($this->debug) {
8991
$this->logger?->error('An exception occured, transforming to an Error resource.', ['exception' => $exception, 'operation' => $apiOperation]);
9092
}
9193

9294
$dup = parent::duplicateRequest($exception, $request);
95+
9396
$operation = $this->initializeExceptionOperation($request, $exception, $format, $apiOperation);
9497

9598
if (null === $operation->getProvider()) {

src/Symfony/Tests/EventListener/ErrorListenerTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,26 @@ public function testDuplicateExceptionWithErrorResource(): void
142142
$errorListener = new ErrorListener('action', null, true, [], $resourceMetadataCollectionFactory, ['jsonld' => ['application/ld+json']], [], $identifiersExtractor, $resourceClassResolver);
143143
$errorListener->onKernelException($exceptionEvent);
144144
}
145+
146+
public function testStateIsNeverModified(): void
147+
{
148+
$resourceClassResolver = $this->createStub(ResourceClassResolverInterface::class);
149+
$kernel = $this->createStub(KernelInterface::class);
150+
$kernel->method('handle')->willReturn(new Response());
151+
152+
$request = Request::create('/');
153+
$request->headers->set('Accept', 'text/html');
154+
$exception = new \Exception();
155+
$exceptionEvent = new ExceptionEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST, $exception);
156+
157+
$initialController = 'initial_controller';
158+
$errorListener = new ErrorListener($initialController, null, false, [], null, ['jsonproblem' => ['application/problem+json']], [], null, $resourceClassResolver);
159+
160+
$errorListener->onKernelException($exceptionEvent);
161+
162+
$refl = new \ReflectionClass($errorListener);
163+
$controllerProp = $refl->getProperty('controller');
164+
165+
$this->assertEquals($initialController, $controllerProp->getValue($errorListener), 'The controller property must never be modified.');
166+
}
145167
}

0 commit comments

Comments
 (0)