vendor/shopware/storefront/Framework/Routing/NotFound/NotFoundSubscriber.php line 164

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Framework\Routing\NotFound;
  3. use Shopware\Core\Framework\Adapter\Cache\AbstractCacheTracer;
  4. use Shopware\Core\Framework\Adapter\Cache\CacheInvalidator;
  5. use Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator;
  6. use Shopware\Core\Framework\Feature;
  7. use Shopware\Core\Framework\Log\Package;
  8. use Shopware\Core\Framework\Uuid\Uuid;
  9. use Shopware\Core\PlatformRequest;
  10. use Shopware\Core\SalesChannelRequest;
  11. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
  12. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceParameters;
  13. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  14. use Shopware\Core\System\SystemConfig\Event\SystemConfigChangedEvent;
  15. use Shopware\Storefront\Controller\ErrorController;
  16. use Shopware\Storefront\Framework\Routing\StorefrontResponse;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\HttpFoundation\RequestStack;
  20. use Symfony\Component\HttpFoundation\Response;
  21. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  22. use Symfony\Component\HttpKernel\Exception\HttpException;
  23. use Symfony\Component\HttpKernel\KernelEvents;
  24. use Symfony\Contracts\Cache\CacheInterface;
  25. use Symfony\Contracts\Cache\ItemInterface;
  26. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  27. /**
  28.  * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  29.  */
  30. #[Package('storefront')]
  31. class NotFoundSubscriber implements EventSubscriberInterface
  32. {
  33.     private const ALL_TAG 'error-page';
  34.     private const SYSTEM_CONFIG_KEY 'core.basicInformation.http404Page';
  35.     private ErrorController $controller;
  36.     private RequestStack $requestStack;
  37.     private SalesChannelContextServiceInterface $contextService;
  38.     private bool $kernelDebug;
  39.     private CacheInterface $cache;
  40.     /**
  41.      * @var AbstractCacheTracer<Response>
  42.      */
  43.     private AbstractCacheTracer $cacheTracer;
  44.     private EntityCacheKeyGenerator $generator;
  45.     private CacheInvalidator $cacheInvalidator;
  46.     private EventDispatcherInterface $eventDispatcher;
  47.     /**
  48.      * @internal
  49.      *
  50.      * @param AbstractCacheTracer<Response> $cacheTracer
  51.      */
  52.     public function __construct(
  53.         ErrorController $controller,
  54.         RequestStack $requestStack,
  55.         SalesChannelContextServiceInterface $contextService,
  56.         bool $kernelDebug,
  57.         CacheInterface $cache,
  58.         AbstractCacheTracer $cacheTracer,
  59.         EntityCacheKeyGenerator $generator,
  60.         CacheInvalidator $cacheInvalidator,
  61.         EventDispatcherInterface $eventDispatcher
  62.     ) {
  63.         $this->controller $controller;
  64.         $this->requestStack $requestStack;
  65.         $this->contextService $contextService;
  66.         $this->kernelDebug $kernelDebug;
  67.         $this->cache $cache;
  68.         $this->cacheTracer $cacheTracer;
  69.         $this->generator $generator;
  70.         $this->cacheInvalidator $cacheInvalidator;
  71.         $this->eventDispatcher $eventDispatcher;
  72.     }
  73.     public static function getSubscribedEvents(): array
  74.     {
  75.         if (Feature::isActive('v6.5.0.0')) {
  76.             return [
  77.                 KernelEvents::EXCEPTION => [
  78.                     ['onError', -100],
  79.                 ],
  80.                 SystemConfigChangedEvent::class => 'onSystemConfigChanged',
  81.             ];
  82.         }
  83.         return [
  84.             SystemConfigChangedEvent::class => 'onSystemConfigChanged',
  85.         ];
  86.     }
  87.     public function onError(ExceptionEvent $event): void
  88.     {
  89.         $request $event->getRequest();
  90.         if ($this->kernelDebug || $request->attributes->has(SalesChannelRequest::ATTRIBUTE_STORE_API_PROXY)) {
  91.             return;
  92.         }
  93.         $event->stopPropagation();
  94.         $salesChannelId $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID'');
  95.         $domainId $request->attributes->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_ID'');
  96.         $languageId $request->attributes->get(PlatformRequest::HEADER_LANGUAGE_ID'');
  97.         if (!$request->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)) {
  98.             // When no sales-channel context is resolved, we need to resolve it now.
  99.             $this->setSalesChannelContext($request);
  100.         }
  101.         $is404StatusCode $event->getThrowable() instanceof HttpException && $event->getThrowable()->getStatusCode() === Response::HTTP_NOT_FOUND;
  102.         // If the exception is not a 404 status code, we don't need to cache it.
  103.         if (!$is404StatusCode) {
  104.             $event->setResponse($this->controller->error(
  105.                 $event->getThrowable(),
  106.                 $request,
  107.                 $event->getRequest()->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)
  108.             ));
  109.             return;
  110.         }
  111.         /** @var SalesChannelContext $context */
  112.         $context $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  113.         $name self::buildName($salesChannelId$domainId$languageId);
  114.         $key $this->generateKey($salesChannelId$domainId$languageId$request$context);
  115.         $response $this->cache->get($key, function (ItemInterface $item) use ($event$name$context) {
  116.             /** @var StorefrontResponse $response */
  117.             $response $this->cacheTracer->trace($name, function () use ($event) {
  118.                 /** @var Request $request */
  119.                 $request $this->requestStack->getMainRequest();
  120.                 return $this->controller->error(
  121.                     $event->getThrowable(),
  122.                     $request,
  123.                     $event->getRequest()->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)
  124.                 );
  125.             });
  126.             $item->tag($this->generateTags($name$event->getRequest(), $context));
  127.             $response->setData(null);
  128.             $response->setContext(null);
  129.             return $response;
  130.         });
  131.         $event->setResponse($response);
  132.     }
  133.     public function onSystemConfigChanged(SystemConfigChangedEvent $event): void
  134.     {
  135.         if ($event->getKey() !== self::SYSTEM_CONFIG_KEY) {
  136.             return;
  137.         }
  138.         $this->cacheInvalidator->invalidate([self::ALL_TAG]);
  139.     }
  140.     /**
  141.      * @deprecated tag:v6.5.0 - reason:visibility-change - method will become private in v6.5.0
  142.      */
  143.     public static function buildName(string $salesChannelIdstring $domainIdstring $languageId): string
  144.     {
  145.         return 'error-page-' $salesChannelId $domainId $languageId;
  146.     }
  147.     private function generateKey(string $salesChannelIdstring $domainIdstring $languageIdRequest $requestSalesChannelContext $context): string
  148.     {
  149.         $key self::buildName($salesChannelId$domainId$languageId) . md5($this->generator->getSalesChannelContextHash($context));
  150.         $event = new NotFoundPageCacheKeyEvent($key$request$context);
  151.         $this->eventDispatcher->dispatch($event);
  152.         return $event->getKey();
  153.     }
  154.     /**
  155.      * @return list<string>
  156.      */
  157.     private function generateTags(string $nameRequest $requestSalesChannelContext $context): array
  158.     {
  159.         $tags array_merge(
  160.             $this->cacheTracer->get($name),
  161.             [$nameself::ALL_TAG]
  162.         );
  163.         $event = new NotFoundPageTagsEvent($tags$request$context);
  164.         $this->eventDispatcher->dispatch($event);
  165.         return array_unique(array_filter($event->getTags()));
  166.     }
  167.     private function setSalesChannelContext(Request $request): void
  168.     {
  169.         $salesChannelId = (string) $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
  170.         $context $this->contextService->get(
  171.             new SalesChannelContextServiceParameters(
  172.                 $salesChannelId,
  173.                 Uuid::randomHex(),
  174.                 $request->headers->get(PlatformRequest::HEADER_LANGUAGE_ID),
  175.                 $request->attributes->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_CURRENCY_ID),
  176.                 $request->attributes->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_ID)
  177.             )
  178.         );
  179.         $request->attributes->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT$context);
  180.     }
  181. }