custom/plugins/MaxiaTaxSwitch6/src/Subscriber/StorefrontSubscriber.php line 80

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Maxia\MaxiaTaxSwitch6\Subscriber;
  3. use Maxia\MaxiaTaxSwitch6\Events\TaxSwitchEvent;
  4. use Maxia\MaxiaTaxSwitch6\Service\TaxSwitchService;
  5. use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
  6. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  7. use Shopware\Core\Framework\Event\BeforeSendResponseEvent;
  8. use Shopware\Core\Framework\Struct\ArrayEntity;
  9. use Shopware\Core\PlatformRequest;
  10. use Shopware\Core\SalesChannelRequest;
  11. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  12. use Shopware\Storefront\Event\StorefrontRenderEvent;
  13. use Shopware\Storefront\Framework\Cache\CacheResponseSubscriber;
  14. use Shopware\Storefront\Framework\Cache\Event\HttpCacheGenerateKeyEvent;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. use Symfony\Component\HttpFoundation\Cookie;
  17. use Symfony\Component\HttpFoundation\RequestStack;
  18. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  19. use Symfony\Component\HttpKernel\KernelEvents;
  20. /**
  21.  * @package Maxia\MaxiaAdvBlockPrices6\Subscriber
  22.  */
  23. class StorefrontSubscriber implements EventSubscriberInterface
  24. {
  25.     public static function getSubscribedEvents(): array
  26.     {
  27.         return [
  28.             StorefrontRenderEvent::class => 'addContextExtension',
  29.             TaxSwitchEvent::class => 'markCartModified',
  30.             KernelEvents::RESPONSE => [
  31.                 ['updateContextHash', -2005],
  32.                 ['updateCacheControl', -1000],
  33.                 ['updateCookie', -1000]
  34.             ],
  35.             BeforeSendResponseEvent::class => [
  36.                 ['removeDuplicateSession', -100],
  37.             ],
  38.             HttpCacheGenerateKeyEvent::class => [
  39.                 'updateHttpCacheKey'
  40.             ]
  41.         ];
  42.     }
  43.     /**
  44.      * @var RequestStack
  45.      */
  46.     private $requestStack;
  47.     /**
  48.      * @var CartService
  49.      */
  50.     private $cartService;
  51.     /**
  52.      * @var TaxSwitchService
  53.      */
  54.     private $taxSwitchService;
  55.     /**
  56.      * @param RequestStack $requestStack
  57.      * @param CartService $cartService
  58.      * @param TaxSwitchService $taxSwitchService
  59.      */
  60.     public function __construct(
  61.         RequestStack $requestStack,
  62.         CartService $cartService,
  63.         TaxSwitchService $taxSwitchService
  64.     ) {
  65.         $this->requestStack $requestStack;
  66.         $this->cartService $cartService;
  67.         $this->taxSwitchService $taxSwitchService;
  68.     }
  69.     /**
  70.      * @param StorefrontRenderEvent $event
  71.      */
  72.     public function addContextExtension(StorefrontRenderEvent $event)
  73.     {
  74.         $context $event->getSalesChannelContext();
  75.         $entity = new ArrayEntity($this->taxSwitchService->getContext($context));
  76.         $context->addExtension('maxiaTaxSwitch'$entity);
  77.     }
  78.     /**
  79.      * @param TaxSwitchEvent $event
  80.      */
  81.     public function markCartModified(TaxSwitchEvent $event)
  82.     {
  83.         $context $event->getSalesChannelContext();
  84.         $taxState $event->getDisplayNet() ? CartPrice::TAX_STATE_NET CartPrice::TAX_STATE_GROSS;
  85.         $context->setTaxState($taxState);
  86.         $context->getCurrentCustomerGroup()->setDisplayGross(!$event->getDisplayNet());
  87.         $cart $this->cartService->getCart($context->getToken(), $contextCartService::SALES_CHANNELfalse);
  88.         $price $cart->getPrice();
  89.         $cart->setPrice(new CartPrice(
  90.             $price->getNetPrice(),
  91.             $price->getTotalPrice(),
  92.             $price->getPositionPrice(),
  93.             $price->getCalculatedTaxes(),
  94.             $price->getTaxRules(),
  95.             $taxState
  96.         ));
  97.         $cart->markModified();
  98.     }
  99.     /**
  100.      * Updates the sw-cache-hash cookie, taking the tax switch setting into account.
  101.      *
  102.      * @param ResponseEvent $event
  103.      */
  104.     public function updateContextHash(ResponseEvent $event): void
  105.     {
  106.         $master $this->getMainRequest();
  107.         $response $event->getResponse();
  108.         if (!$master ||
  109.             !$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST) ||
  110.             !$master->attributes->get(TaxSwitchService::TAX_SWITCH_ACTIVE_ATTR)) {
  111.             return;
  112.         }
  113.         /** @var SalesChannelContext $context */
  114.         $context $master->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  115.         if (!$context) {
  116.             return;
  117.         }
  118.         $isNet $this->taxSwitchService->getDisplayNet($context);
  119.         if ($isNet === null) {
  120.             return;
  121.         }
  122.         $cookieName CacheResponseSubscriber::CONTEXT_CACHE_COOKIE;
  123.         $currentHash $master->cookies->get($cookieName);
  124.         $newHash $this->buildCacheHash($context$isNet);
  125.         // remove context cookie from headers if already set
  126.         $responseHash null;
  127.         $headers $response->headers->all();
  128.         if (isset($headers['set-cookie'])) {
  129.             foreach ($headers['set-cookie'] as $key => $value) {
  130.                 if (preg_match('/' preg_quote($cookieName'/') . '=(.+);/'$value$matches)) {
  131.                     unset($headers['set-cookie'][$key]);
  132.                     $response->headers->set('set-cookie'$headers['set-cookie']);
  133.                     $responseHash $matches[1];
  134.                     break;
  135.                 }
  136.             }
  137.         }
  138.         // update cookie if needed
  139.         if ($currentHash !== $newHash || ($responseHash && $responseHash !== $newHash)) {
  140.             $cookie Cookie::create($cookieName$newHash);
  141.             $cookie->setSecureDefault($master->isSecure());
  142.             $response->headers->setCookie($cookie);
  143.         }
  144.     }
  145.     /**
  146.      * @param ResponseEvent $event
  147.      */
  148.     public function updateCacheControl(ResponseEvent $event): void
  149.     {
  150.         $master $this->getMainRequest();
  151.         $response $event->getResponse();
  152.         if (!$master ||
  153.             !$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST) ||
  154.             !$master->attributes->get(TaxSwitchService::TAX_SWITCH_ACTIVE_ATTR)) {
  155.             return;
  156.         }
  157.         /** @var SalesChannelContext $context */
  158.         $context $master->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  159.         if ($context && $this->taxSwitchService->getConfig($context)['disableClientCache']) {
  160.             $cacheControl $response->headers->get('Cache-Control''');
  161.             $cacheControl explode(','$cacheControl);
  162.             $cacheControl[] = 'no-store';
  163.             $cacheControl[] = 'must-revalidate';
  164.             $response->headers->set('Cache-Control'implode(', 'array_unique($cacheControl)));
  165.         }
  166.     }
  167.     /**
  168.      * Updates the tax switch cookie, if the setting has been changed.
  169.      * Will be ignored, if the cookie was not set beforehand on the client side.
  170.      *
  171.      * @param ResponseEvent $event
  172.      */
  173.     public function updateCookie(ResponseEvent $event): void
  174.     {
  175.         $master $this->getMainRequest();
  176.         if (!$master || !$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
  177.             return;
  178.         }
  179.         if (!$master
  180.             || !$master->attributes->get(TaxSwitchService::TAX_SWITCH_ACTIVE_ATTR)
  181.             || !$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)
  182.             || !$master->attributes->get(TaxSwitchService::STATE_CHANGED_ATTR)
  183.             || $master->cookies->get(TaxSwitchService::COOKIE_NAME) === null)
  184.         {
  185.             return;
  186.         }
  187.         /** @var SalesChannelContext $context */
  188.         $context $master->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  189.         if (!$context) {
  190.             return;
  191.         }
  192.         $isNet $this->taxSwitchService->getDisplayNet($context);
  193.         if ($isNet === null) {
  194.             return;
  195.         }
  196.         $cookie Cookie::create(TaxSwitchService::COOKIE_NAME, (string)((int)$isNet))
  197.             ->withHttpOnly(false)
  198.             ->withExpires((new \DateTime())->add(new \DateInterval('P30D')));
  199.         $cookie->setSecureDefault($master->isSecure());
  200.         $event->getResponse()->headers->setCookie($cookie);
  201.     }
  202.     /**
  203.      * Add tax state to http cache key
  204.      *
  205.      * @param HttpCacheGenerateKeyEvent $event
  206.      * @return void
  207.      */
  208.     public function updateHttpCacheKey(HttpCacheGenerateKeyEvent $event)
  209.     {
  210.         $isNet $event->getRequest()->cookies->get(TaxSwitchService::COOKIE_NAME);
  211.         if ($isNet !== null) {
  212.             $event->setHash(hash('sha256'$event->getHash() . '-' . ($isNet 0)));
  213.         }
  214.     }
  215.     /**
  216.      * Remove the duplicate session cookie, if it is being set.
  217.      * https://issues.shopware.com/issues/NEXT-10238
  218.      * @todo Remove when possible
  219.      *
  220.      * @param BeforeSendResponseEvent $event
  221.      */
  222.     public function removeDuplicateSession(BeforeSendResponseEvent $event): void
  223.     {
  224.         if (!$event->getRequest()->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
  225.             return;
  226.         }
  227.         $hasSessionCookie false;
  228.         if (session_id() && headers_list()) {
  229.             $cookies array_filter(headers_list(), function ($item) {
  230.                 return strpos($item'session-='.session_id()) !== false;
  231.             });
  232.             $hasSessionCookie count($cookies) > 0;
  233.         }
  234.         $response $event->getResponse();
  235.         $headers $response->headers->all();
  236.         if (isset($headers['set-cookie'])) {
  237.             foreach ($headers['set-cookie'] as $key => $value) {
  238.                 if (str_starts_with($value'PHPSESSID=')) {
  239.                     unset($headers['set-cookie'][$key]);
  240.                 }
  241.                 if ($hasSessionCookie && str_starts_with($value'session-=')) {
  242.                     if (strpos($valuesession_id()) === false) {
  243.                         unset($headers['set-cookie'][$key]);
  244.                     }
  245.                 }
  246.             }
  247.             if ($headers['set-cookie']) {
  248.                 $response->headers->set('set-cookie'$headers['set-cookie']);
  249.                 $event->setResponse($response);
  250.             }
  251.         }
  252.     }
  253.     /**
  254.      * Returns the value for the sw-cache-hash cookie.
  255.      * @todo Extend SW implementation when possible
  256.      *
  257.      * @param SalesChannelContext $context
  258.      * @param bool $isNet
  259.      * @return string
  260.      */
  261.     protected function buildCacheHash(SalesChannelContext $contextbool $isNet): string
  262.     {
  263.         $data = [
  264.             $context->getRuleIds(),
  265.             $context->getContext()->getVersionId(),
  266.             $context->getCurrency()->getId(),
  267.             $isNet
  268.         ];
  269.         $acrisCacheHashExtension $context->getExtension('acris_cache_hash');
  270.         if ($acrisCacheHashExtension) {
  271.             $acrisCacheHash md5(json_encode($acrisCacheHashExtension->all()));
  272.             $data[] = $acrisCacheHash;
  273.         }
  274.         return md5(json_encode($data));
  275.     }
  276.     /**
  277.      * @return \Symfony\Component\HttpFoundation\Request|null
  278.      */
  279.     protected function getMainRequest()
  280.     {
  281.         return method_exists($this->requestStack'getMainRequest')
  282.             ? $this->requestStack->getMainRequest()
  283.             : $this->requestStack->getMasterRequest();
  284.     }
  285. }