<?php
namespace Mm\Escmid\EaaBundle\EventListener;
use Mm\Escmid\EaaBundle\EventListener\ExceptionHandler\ExceptionHandlerDefinition;
use Mm\Escmid\EaaBundle\EventListener\ExceptionHandler\ExceptionHandlerInterface;
use Mm\Escmid\EaaBundle\Platform\Security\AuthenticationException;
use Mm\Escmid\EaaBundle\Platform\Service\ExceptionResponseGeneratorServiceInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\KernelInterface;
class ExceptionListener implements EventSubscriberInterface
{
public function __construct(
private readonly KernelInterface $kernel,
private readonly ExceptionResponseGeneratorServiceInterface $responseGeneratorService,
private readonly array $handlerDefinitions,
private ExceptionHandlerInterface $fallbackHandler
) {
}
public function checkIfDefinitionContainsKey(ExceptionHandlerDefinition $handlerDefinition, string $key): bool
{
return in_array($key, $handlerDefinition->getKeys(), true);
}
public function getFallthruHandlerDefinition(): ?ExceptionHandlerDefinition
{
return $this->getHandlerDefinitionForKey('fallthru_'.$this->kernel->getEnvironment());
}
public function getHandlerDefinitionForKey(string $key): ?ExceptionHandlerDefinition
{
foreach ($this->handlerDefinitions as $definition) {
if ($this->checkIfDefinitionContainsKey($definition, $key)) {
return $definition;
}
}
return null;
}
/**
* Get exception handler for event.
*/
public function getHandlerForException(\Throwable $exception): ExceptionHandlerInterface
{
$exceptionClassName = $exception::class;
$handlerDefinition = $this->getHandlerDefinitionForKey($exceptionClassName);
if (!$handlerDefinition) {
$handlerDefinition = $this->getFallthruHandlerDefinition();
}
if (!$handlerDefinition) {
$exceptionHandler = $this->fallbackHandler;
} else {
$exceptionHandler = $handlerDefinition->getExceptionHandler();
}
return $exceptionHandler;
}
/**
* Handle kernel exceptions.
*/
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
$previousException = $exception->getPrevious();
if (
!($exception instanceof AuthenticationException)
&& !($exception instanceof AccessDeniedHttpException)
&& ($previousException instanceof \Exception)
) {
$exception = $previousException;
}
$exceptionHandler = $this->getHandlerForException($exception);
$responseData = $exceptionHandler->handle($event);
$event->setResponse($this->responseGeneratorService->generateResponse($responseData));
}
/**
* @return array<string, mixed>
*/
public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => ['onKernelException', -1],
];
}
}