vendor/shopware/storefront/Framework/Routing/StorefrontSubscriber.php line 245

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Framework\Routing;
  3. use Shopware\Core\Checkout\Cart\Exception\CustomerNotLoggedInException;
  4. use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
  5. use Shopware\Core\Checkout\Customer\Event\CustomerLogoutEvent;
  6. use Shopware\Core\Content\Seo\HreflangLoaderInterface;
  7. use Shopware\Core\Content\Seo\HreflangLoaderParameter;
  8. use Shopware\Core\Framework\App\ActiveAppsLoader;
  9. use Shopware\Core\Framework\App\Exception\AppUrlChangeDetectedException;
  10. use Shopware\Core\Framework\App\ShopId\ShopIdProvider;
  11. use Shopware\Core\Framework\Event\BeforeSendResponseEvent;
  12. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  13. use Shopware\Core\Framework\Routing\KernelListenerPriorities;
  14. use Shopware\Core\Framework\Util\Random;
  15. use Shopware\Core\PlatformRequest;
  16. use Shopware\Core\SalesChannelRequest;
  17. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
  18. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  19. use Shopware\Storefront\Controller\ErrorController;
  20. use Shopware\Storefront\Event\StorefrontRenderEvent;
  21. use Shopware\Storefront\Framework\Csrf\CsrfPlaceholderHandler;
  22. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  23. use Symfony\Component\HttpFoundation\RedirectResponse;
  24. use Symfony\Component\HttpFoundation\RequestStack;
  25. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  26. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  27. use Symfony\Component\HttpKernel\Event\RequestEvent;
  28. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  29. use Symfony\Component\HttpKernel\KernelEvents;
  30. use Symfony\Component\Routing\RouterInterface;
  31. class StorefrontSubscriber implements EventSubscriberInterface
  32. {
  33.     /**
  34.      * @var RequestStack
  35.      */
  36.     private $requestStack;
  37.     /**
  38.      * @var RouterInterface
  39.      */
  40.     private $router;
  41.     /**
  42.      * @var ErrorController
  43.      */
  44.     private $errorController;
  45.     /**
  46.      * @var SalesChannelContextServiceInterface
  47.      */
  48.     private $contextService;
  49.     /**
  50.      * @var bool
  51.      */
  52.     private $kernelDebug;
  53.     /**
  54.      * @var CsrfPlaceholderHandler
  55.      */
  56.     private $csrfPlaceholderHandler;
  57.     /**
  58.      * @var MaintenanceModeResolver
  59.      */
  60.     private $maintenanceModeResolver;
  61.     /**
  62.      * @var HreflangLoaderInterface
  63.      */
  64.     private $hreflangLoader;
  65.     /**
  66.      * @var ShopIdProvider
  67.      */
  68.     private $shopIdProvider;
  69.     /**
  70.      * @var ActiveAppsLoader
  71.      */
  72.     private $activeAppsLoader;
  73.     public function __construct(
  74.         RequestStack $requestStack,
  75.         RouterInterface $router,
  76.         ErrorController $errorController,
  77.         SalesChannelContextServiceInterface $contextService,
  78.         CsrfPlaceholderHandler $csrfPlaceholderHandler,
  79.         HreflangLoaderInterface $hreflangLoader,
  80.         bool $kernelDebug,
  81.         MaintenanceModeResolver $maintenanceModeResolver,
  82.         ShopIdProvider $shopIdProvider,
  83.         ActiveAppsLoader $activeAppsLoader
  84.     ) {
  85.         $this->requestStack $requestStack;
  86.         $this->router $router;
  87.         $this->errorController $errorController;
  88.         $this->contextService $contextService;
  89.         $this->kernelDebug $kernelDebug;
  90.         $this->csrfPlaceholderHandler $csrfPlaceholderHandler;
  91.         $this->maintenanceModeResolver $maintenanceModeResolver;
  92.         $this->hreflangLoader $hreflangLoader;
  93.         $this->shopIdProvider $shopIdProvider;
  94.         $this->activeAppsLoader $activeAppsLoader;
  95.     }
  96.     public static function getSubscribedEvents(): array
  97.     {
  98.         return [
  99.             KernelEvents::REQUEST => [
  100.                 ['startSession'40],
  101.                 ['maintenanceResolver'],
  102.             ],
  103.             KernelEvents::EXCEPTION => [
  104.                 ['showHtmlExceptionResponse', -100],
  105.                 ['customerNotLoggedInHandler'],
  106.                 ['maintenanceResolver'],
  107.             ],
  108.             KernelEvents::CONTROLLER => [
  109.                 ['preventPageLoadingFromXmlHttpRequest'KernelListenerPriorities::KERNEL_CONTROLLER_EVENT_SCOPE_VALIDATE],
  110.             ],
  111.             CustomerLoginEvent::class => [
  112.                 'updateSessionAfterLogin',
  113.             ],
  114.             CustomerLogoutEvent::class => [
  115.                 'updateSessionAfterLogout',
  116.             ],
  117.             BeforeSendResponseEvent::class => [
  118.                 ['replaceCsrfToken'],
  119.                 ['setCanonicalUrl'],
  120.             ],
  121.             StorefrontRenderEvent::class => [
  122.                 ['addHreflang'],
  123.                 ['addShopIdParameter'],
  124.             ],
  125.         ];
  126.     }
  127.     public function startSession(): void
  128.     {
  129.         $master $this->requestStack->getMasterRequest();
  130.         if (!$master) {
  131.             return;
  132.         }
  133.         if (!$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
  134.             return;
  135.         }
  136.         if (!$master->hasSession()) {
  137.             return;
  138.         }
  139.         $session $master->getSession();
  140.         $applicationId $master->attributes->get(PlatformRequest::ATTRIBUTE_OAUTH_CLIENT_ID);
  141.         if (!$session->isStarted()) {
  142.             $session->setName('session-' $applicationId);
  143.             $session->start();
  144.             $session->set('sessionId'$session->getId());
  145.         }
  146.         $salesChannelId $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
  147.         if ($salesChannelId === null) {
  148.             /** @var SalesChannelContext|null $salesChannelContext */
  149.             $salesChannelContext $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  150.             if ($salesChannelContext !== null) {
  151.                 $salesChannelId $salesChannelContext->getSalesChannel()->getId();
  152.             }
  153.         }
  154.         if (!$session->has(PlatformRequest::HEADER_CONTEXT_TOKEN) || $session->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID) !== $salesChannelId) {
  155.             $token Random::getAlphanumericString(32);
  156.             $session->set(PlatformRequest::HEADER_CONTEXT_TOKEN$token);
  157.             $session->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID$salesChannelId);
  158.         }
  159.         $master->headers->set(
  160.             PlatformRequest::HEADER_CONTEXT_TOKEN,
  161.             $session->get(PlatformRequest::HEADER_CONTEXT_TOKEN)
  162.         );
  163.     }
  164.     public function updateSessionAfterLogin(CustomerLoginEvent $event): void
  165.     {
  166.         $token $event->getContextToken();
  167.         $this->updateSession($token);
  168.     }
  169.     public function updateSessionAfterLogout(CustomerLogoutEvent $event): void
  170.     {
  171.         $newToken $event->getSalesChannelContext()->getToken();
  172.         $this->updateSession($newToken);
  173.     }
  174.     public function updateSession(string $token): void
  175.     {
  176.         $master $this->requestStack->getMasterRequest();
  177.         if (!$master) {
  178.             return;
  179.         }
  180.         if (!$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
  181.             return;
  182.         }
  183.         if (!$master->hasSession()) {
  184.             return;
  185.         }
  186.         $session $master->getSession();
  187.         $session->migrate();
  188.         $session->set('sessionId'$session->getId());
  189.         $session->set(PlatformRequest::HEADER_CONTEXT_TOKEN$token);
  190.         $master->headers->set(PlatformRequest::HEADER_CONTEXT_TOKEN$token);
  191.     }
  192.     public function showHtmlExceptionResponse(ExceptionEvent $event): void
  193.     {
  194.         if ($this->kernelDebug) {
  195.             return;
  196.         }
  197.         if (!$event->getRequest()->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)) {
  198.             //When no saleschannel context is resolved, we need to resolve it now.
  199.             $this->setSalesChannelContext($event);
  200.         }
  201.         if ($event->getRequest()->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)) {
  202.             $event->stopPropagation();
  203.             $response $this->errorController->error(
  204.                 $event->getThrowable(),
  205.                 $this->requestStack->getMasterRequest(),
  206.                 $event->getRequest()->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)
  207.             );
  208.             $event->setResponse($response);
  209.         }
  210.     }
  211.     public function customerNotLoggedInHandler(ExceptionEvent $event): void
  212.     {
  213.         if (!$event->getRequest()->attributes->has(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
  214.             return;
  215.         }
  216.         if (!$event->getThrowable() instanceof CustomerNotLoggedInException) {
  217.             return;
  218.         }
  219.         $request $event->getRequest();
  220.         $parameters = [
  221.             'redirectTo' => $request->attributes->get('_route'),
  222.             'redirectParameters' => json_encode($request->attributes->get('_route_params')),
  223.         ];
  224.         $redirectResponse = new RedirectResponse($this->router->generate('frontend.account.login.page'$parameters));
  225.         $event->setResponse($redirectResponse);
  226.     }
  227.     public function maintenanceResolver(RequestEvent $event): void
  228.     {
  229.         if ($this->maintenanceModeResolver->shouldRedirect($event->getRequest())) {
  230.             $event->setResponse(
  231.                 new RedirectResponse(
  232.                     $this->router->generate('frontend.maintenance.page'),
  233.                     RedirectResponse::HTTP_TEMPORARY_REDIRECT
  234.                 )
  235.             );
  236.         }
  237.     }
  238.     public function preventPageLoadingFromXmlHttpRequest(ControllerEvent $event): void
  239.     {
  240.         if (!$event->getRequest()->isXmlHttpRequest()) {
  241.             return;
  242.         }
  243.         /** @var RouteScope $scope */
  244.         $scope $event->getRequest()->attributes->get(PlatformRequest::ATTRIBUTE_ROUTE_SCOPE, new RouteScope(['scopes' => []]));
  245.         if (!$scope->hasScope(StorefrontRouteScope::ID)) {
  246.             return;
  247.         }
  248.         $controller $event->getController();
  249.         // happens if Controller is a closure
  250.         if (!is_array($controller)) {
  251.             return;
  252.         }
  253.         $isAllowed $event->getRequest()->attributes->getBoolean('XmlHttpRequest'false);
  254.         if ($isAllowed) {
  255.             return;
  256.         }
  257.         throw new AccessDeniedHttpException('PageController can\'t be requested via XmlHttpRequest.');
  258.     }
  259.     public function setCanonicalUrl(BeforeSendResponseEvent $event): void
  260.     {
  261.         if (!$event->getResponse()->isSuccessful()) {
  262.             return;
  263.         }
  264.         if ($canonical $event->getRequest()->attributes->get(SalesChannelRequest::ATTRIBUTE_CANONICAL_LINK)) {
  265.             $canonical sprintf('<%s>; rel="canonical"'$canonical);
  266.             $event->getResponse()->headers->set('Link'$canonical);
  267.         }
  268.     }
  269.     public function replaceCsrfToken(BeforeSendResponseEvent $event): void
  270.     {
  271.         $event->setResponse(
  272.             $this->csrfPlaceholderHandler->replaceCsrfToken($event->getResponse(), $event->getRequest())
  273.         );
  274.     }
  275.     public function addHreflang(StorefrontRenderEvent $event): void
  276.     {
  277.         $request $event->getRequest();
  278.         $route $request->attributes->get('_route');
  279.         if ($route === null) {
  280.             return;
  281.         }
  282.         $routeParams $request->attributes->get('_route_params', []);
  283.         $salesChannelContext $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  284.         $parameter = new HreflangLoaderParameter($route$routeParams$salesChannelContext);
  285.         $event->setParameter('hrefLang'$this->hreflangLoader->load($parameter));
  286.     }
  287.     public function addShopIdParameter(StorefrontRenderEvent $event): void
  288.     {
  289.         if (!$this->activeAppsLoader->getActiveApps()) {
  290.             return;
  291.         }
  292.         try {
  293.             $shopId $this->shopIdProvider->getShopId();
  294.         } catch (AppUrlChangeDetectedException $e) {
  295.             return;
  296.         }
  297.         $event->setParameter('appShopId'$shopId);
  298.         /*
  299.          * @deprecated tag:v6.4.0 use `appShopId` instead
  300.          */
  301.         $event->setParameter('swagShopId'$shopId);
  302.     }
  303.     private function setSalesChannelContext(ExceptionEvent $event): void
  304.     {
  305.         $contextToken $event->getRequest()->headers->get(PlatformRequest::HEADER_CONTEXT_TOKEN);
  306.         $salesChannelId $event->getRequest()->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
  307.         $context $this->contextService->get(
  308.             $salesChannelId,
  309.             $contextToken,
  310.             $event->getRequest()->headers->get(PlatformRequest::HEADER_LANGUAGE_ID),
  311.             $event->getRequest()->attributes->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_CURRENCY_ID)
  312.         );
  313.         $event->getRequest()->attributes->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT$context);
  314.     }
  315. }