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

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