<?php
declare(strict_types=1);
namespace Slivki\EventSubscriber;
use Slivki\Controller\Admin\BePaid\ValidateBePaidCredentialsAction;
use Slivki\Controller\Api\OnlineOrder\Vendor\GetNearestTimeAction;
use Slivki\Controller\Payment\Click\CompletePaymentAction;
use Slivki\Controller\Payment\Click\PreparePaymentAction;
use Slivki\Controller\Subscription\GetChildSubscribersAction;
use Slivki\Controller\Subscription\ShareSubscriptionAction;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use function json_decode;
final class ExceptionListener
{
private const API_NAMESPACES_START = [
'Api\V2',
'UserController::loginAction',
'Api\Partner',
'Api\Scheduler',
'Admin\Api',
'OnlineOrder\GiftCertificate\EditGiftCertificateOnlineOrderAction',
'MobileApi\Partner',
'Api\OnlineOrder\Payment\Method',
ValidateBePaidCredentialsAction::class,
GetNearestTimeAction::class,
ShareSubscriptionAction::class,
GetChildSubscribersAction::class,
PreparePaymentAction::class,
CompletePaymentAction::class,
];
public function onKernelException(ExceptionEvent $event): void
{
$exception = $this->getException($event->getThrowable());
$exceptionMessage = $exception->getMessage();
$controller = $event->getRequest()->attributes->get('_controller');
if (null !== $controller && $this->isIncludedToRoute($controller)) {
json_decode($exceptionMessage, true);
$isJson = \json_last_error() === JSON_ERROR_NONE;
$response = new JsonResponse(
$isJson ? $exceptionMessage : ['error' => $exceptionMessage],
$exception->getCode() !== 0 ? $exception->getCode() : Response::HTTP_INTERNAL_SERVER_ERROR,
[],
$isJson
);
if ($exception instanceof HttpExceptionInterface) {
$response->setStatusCode($exception->getStatusCode());
$response->headers->replace($exception->getHeaders());
}
$event->setResponse($response);
}
}
private function isIncludedToRoute(string $controller): bool
{
$matches = \array_filter(
self::API_NAMESPACES_START,
static fn(string $needle): bool => strpos($controller, $needle) !== false,
);
return \count($matches) > 0;
}
private function getException(\Throwable $exception): \Throwable
{
return $exception instanceof HandlerFailedException
? $this->getException($exception->getPrevious())
: $exception;
}
}