<?php declare(strict_types=1);
namespace SwagB2bPlatform\Routing;
use LogicException;
use Shopware\B2B\Common\Controller\B2bControllerForwardException;
use Shopware\B2B\Common\Controller\B2bControllerRedirectException;
use Shopware\B2B\Common\Controller\B2bControllerRouteNameProvider;
use Shopware\B2B\Common\Controller\B2bControllerRoutingException;
use Shopware\Core\Framework\Routing\RequestTransformerInterface;
use Shopware\Storefront\Framework\Routing\Router;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use function array_merge;
use function get_class;
class ControllerRoutingExceptionHandler implements EventSubscriberInterface
{
/**
* @var RouterInterface
*/
private $router;
/**
* @var HttpKernelInterface
*/
private $httpKernel;
/**
* @var RequestTransformerInterface
*/
private $requestTransformer;
public function __construct(
RouterInterface $router,
HttpKernelInterface $httpKernel,
RequestTransformerInterface $requestTransformer
) {
$this->router = $router;
$this->httpKernel = $httpKernel;
$this->requestTransformer = $requestTransformer;
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => ['handleRouting', 500],
];
}
public function handleRouting(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
$request = $event->getRequest();
if (!$exception instanceof B2bControllerRoutingException) {
return;
}
if ($exception instanceof B2bControllerForwardException) {
$this->updateEvent($event, $this->handleForward($exception, $request));
return;
}
if ($exception instanceof B2bControllerRedirectException) {
$this->updateEvent($event, $this->handleRedirect($exception, $request));
return;
}
throw new LogicException('Unable to handle ' . get_class($exception));
}
/**
* @internal
*/
protected function updateEvent(ExceptionEvent $event, Response $response): void
{
$event->allowCustomResponseCode();
$event->setResponse($response);
$event->stopPropagation();
}
/**
* @internal
*/
protected function handleRedirect(B2bControllerRedirectException $redirectException, Request $request): Response
{
$url = $this->generateUrl($redirectException, $request);
return new RedirectResponse($url);
}
/**
* @internal
*/
protected function handleForward(B2bControllerForwardException $forwardException, Request $request): Response
{
$route = $this->assembleRouteName($forwardException, $request);
$url = $this->router
->generate($route, [], Router::PATH_INFO);
$subRequest = $this->cloneRequest($forwardException, $request, $url);
return $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
/**
* @internal
*/
protected function assembleRouteName(B2bControllerRoutingException $routingException, Request $request): string
{
if ($routingException instanceof B2bControllerRouteNameProvider) {
return $routingException->getRouteName();
}
$controller = $routingException->getController();
if ($controller === null) {
$routeParams = $request->attributes->get('_route_params');
$controller = $routeParams[RouteLoader::ROUTE_CONTROLLER_ROUTE_NAME];
}
$action = $routingException->getAction();
return 'frontend.b2b.' . $controller . '.' . $action;
}
/**
* @internal
*/
protected function cloneRequest(B2bControllerForwardException $forwardException, Request $request, string $url): Request
{
$requestContext = $this->router
->getContext();
$currentMethod = $requestContext
->getMethod();
$requestContext
->setMethod(Request::METHOD_GET);
$route = $this->router->match($url);
$requestContext->setMethod($currentMethod);
$route['_route_params'] = $route;
$route = array_merge(
$this->requestTransformer->extractInheritableAttributes($request),
$route
);
return $request->duplicate($forwardException->getParams(), null, $route);
}
/**
* @internal
*/
protected function generateUrl(B2bControllerRoutingException $e, Request $request): string
{
$route = $this->assembleRouteName($e, $request);
return $this->router
->generate($route, $e->getParams(), UrlGeneratorInterface::ABSOLUTE_PATH);
}
}