custom/plugins/ScopPlatformRedirecter/src/Subscriber/RequestSubscriber.php line 70

Open in your IDE?
  1. <?php
  2. /**
  3.  * Implemented by scope01 GmbH team https://scope01.com
  4.  *
  5.  * @copyright scope01 GmbH https://scope01.com
  6.  * @license MIT License
  7.  * @link https://scope01.com
  8.  */
  9. declare(strict_types=1);
  10. /**
  11.  * Implemented by scope01 GmbH team https://scope01.com
  12.  *
  13.  * @copyright scope01 GmbH https://scope01.com
  14.  * @license MIT
  15.  * @link https://scope01.com
  16.  */
  17. namespace Scop\PlatformRedirecter\Subscriber;
  18. use Shopware\Core\Framework\Context;
  19. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  23. use Shopware\Core\Framework\Event\BeforeSendRedirectResponseEvent;
  24. use Shopware\Core\Framework\Event\BeforeSendResponseEvent;
  25. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  26. use Symfony\Component\HttpFoundation\RedirectResponse;
  27. class RequestSubscriber implements EventSubscriberInterface
  28. {
  29.     /**
  30.      * @var EntityRepositoryInterface
  31.      */
  32.     private $repository;
  33.     /**
  34.      * @var EntityRepositoryInterface
  35.      */
  36.     private $seoUrlRepository;
  37.     /**
  38.      * @param EntityRepositoryInterface $redirectRepository
  39.      */
  40.     public function __construct(EntityRepositoryInterface $redirectRepository, EntityRepositoryInterface $seoUrlRepository)
  41.     {
  42.         /** @var EntityRepositoryInterface $repository */
  43.         $this->repository = $redirectRepository;
  44.         $this->seoUrlRepository = $seoUrlRepository;
  45.     }
  46.     /**
  47.      *
  48.      * @return array
  49.      */
  50.     public static function getSubscribedEvents(): array
  51.     {
  52.         return [
  53.             BeforeSendResponseEvent::class => 'redirectBeforeSendResponse',
  54.             BeforeSendRedirectResponseEvent::class => 'redirectBeforeSendResponse'
  55.         ];
  56.     }
  57.     /**
  58.      * Redirect to the new url if found in redirects
  59.      * Otherwise do nothing
  60.      * Modules like admin, api or widgets are excluded from redirects
  61.      *
  62.      * @param BeforeSendResponseEvent $event
  63.      */
  64.     public function redirectBeforeSendResponse(BeforeSendResponseEvent $event): void
  65.     {
  66.         $requestUri = (string)$event->getRequest()->get("sw-original-request-uri");
  67.         // "sw-original-request-uri" is not present in shopware versions below 6.4.0.0.
  68.         // In this case, an older method must be used for redirecting, which doesn't redirects correctly in some edge cases.
  69.         if ($requestUri === "") {
  70.             $this->oldRedirectBeforeSendResponse($event);
  71.             return;
  72.         }
  73.         $storefrontUri = $event->getRequest()->get('sw-sales-channel-absolute-base-url');
  74.         $requestBase = $event->getRequest()->getPathInfo();
  75.         $requestBaseUrl = $event->getRequest()->getBaseUrl();
  76.         // Block overriding /admin and /api and widgets, so you can't lock out of the administration.
  77.         if (\strpos($requestBase, "/admin") === 0) {
  78.             return;
  79.         }
  80.         if (\strpos($requestBase, "/api") === 0) {
  81.             return;
  82.         }
  83.         if (\strpos($requestBase, "/widgets") === 0) {
  84.             return;
  85.         }
  86.         if (\strpos($requestBase, "/store-api") === 0) {
  87.             return;
  88.         }
  89.         if (\strpos($requestBase, "/_profiler") === 0) {
  90.             return;
  91.         }
  92.         $context = Context::createDefaultContext();
  93.         $umlaute = [
  94.             '%C3%9C' => 'Ü',
  95.             '%C3%BC' => 'ü',
  96.             '%C3%96' => 'Ö',
  97.             '%C3%B6' => 'ö',
  98.             '%C3%84' => 'Ä',
  99.             '%C3%A4' => 'ä',
  100.             '%C3%9F' => 'ß'
  101.         ];
  102.         foreach ($umlaute as $key => $value) {
  103.             $requestUri = str_replace($key, $value, $requestUri);
  104.         }
  105.         $search = [
  106.             $requestBaseUrl . '/' . $requestUri, // relative url with shopware 6 in sub folder: /public/Ergonomic-Concrete-Cough-Machine/48314803f1244f609a2ce907bfb48f53
  107.             $requestBaseUrl . $requestUri, // relative url with shopware 6 in sub folder url is not shopware seo url: /public/test
  108.             $storefrontUri . $requestUri, // absolute url with shopware 6 in sub folder, full url with host: http://shopware-platform.local/public/test1
  109.             $storefrontUri . '/' . $requestUri, // absolute url with shopware 6 in sub folder, full url with host and slash at the end: http://shopware-platform.local/public/Freizeit-Elektro/Telefone/
  110.             $requestUri, // relative url domain configured in public folder: /Ergonomic-Concrete-Cough-Machine/48314803f1244f609a2ce907bfb48f53 or /test4
  111.             '/' . $requestUri, // absolute url domain configured in public folder: http://shopware-platform.local/Shoes-Baby/
  112.             \substr($requestUri, 1), // e.g. "test"
  113.         ];
  114.         // search for the redirect in the database
  115.         $redirects = $this->repository->search((new Criteria())->addFilter(new EqualsAnyFilter('sourceURL', $search))->addFilter(new EqualsFilter('enabled', true))
  116.             ->setLimit(1), $context);
  117.         if ($redirects->count() === 0) {
  118.             // Checks if the requested URL contains Query parameters, and if so, checks if a redirect can be found with the ignoreQueryParams option
  119.             if (str_contains($requestUri, '?')) {
  120.                 $searchWithoutQuery = [];
  121.                 foreach ($search as $string)
  122.                     $searchWithoutQuery[] = explode('?', $string)[0];
  123.                 $redirects = $this->repository->search((new Criteria())->addFilter(new EqualsAnyFilter('sourceURL', $searchWithoutQuery))->addFilter(new EqualsFilter('enabled', true))->addFilter(new EqualsFilter('ignoreQueryParams', true))
  124.                     ->setLimit(1), $context);
  125.                 // No Redirect found for this URL, do nothing
  126.                 if ($redirects->count() === 0) {
  127.                     return;
  128.                 }
  129.             } else {
  130.                 // No Redirect found for this URL, do nothing
  131.                 return;
  132.             }
  133.         }
  134.         $redirect = $redirects->first();
  135.         $targetURL = $redirect->getTargetURL();
  136.         $code = $redirect->getHttpCode();
  137.         // Prevent endless redirecting when target url and source url have only different capitalisation
  138.         if (in_array($targetURL, $search, true)) {
  139.             return;
  140.         }
  141.         /*
  142.          *  checks if $targetURL is a full url or path and redirects accordingly
  143.          */
  144.         if (!(\strpos($targetURL, "http:") === 0 || \strpos($targetURL, "https:") === 0)) {
  145.             if (\strpos($targetURL, "www.") === 0) {
  146.                 $targetURL = "http://" . $targetURL;
  147.             } else {
  148.                 if (\strpos($targetURL, "/") !== 0) {
  149.                     $targetURL = "/" . $targetURL;
  150.                 }
  151.             }
  152.         }
  153.         $event->setResponse(new RedirectResponse($targetURL, $code));
  154.     }
  155.     /**
  156.      * Method for shopware versions below 6.4.0.0
  157.      *
  158.      * Redirect to the new url if found in redirects
  159.      * Otherwise do nothing
  160.      * Modules like admin, api or widgets are excluded from redirects
  161.      *
  162.      * @param BeforeSendResponseEvent $event
  163.      */
  164.     private function oldRedirectBeforeSendResponse(BeforeSendResponseEvent $event): void
  165.     {
  166.         $requestUri = (string)$event->getRequest()->get('resolved-uri');
  167.         $storefrontUri = $event->getRequest()->get('sw-storefront-url');
  168.         $requestBase = $event->getRequest()->getPathInfo();
  169.         $requestBaseUrl = $event->getRequest()->getBaseUrl();
  170.         $queryString = (string)$event->getRequest()->getQueryString();
  171.         $search = [];
  172.         // Block overriding /admin and /api and widgets, so you can't lock out of the administration.
  173.         if (\strpos($requestBase, "/admin") === 0) {
  174.             return;
  175.         }
  176.         if (\strpos($requestBase, "/api") === 0) {
  177.             return;
  178.         }
  179.         if (\strpos($requestBase, "/widgets") === 0) {
  180.             return;
  181.         }
  182.         if (\strpos($requestBase, "/store-api") === 0) {
  183.             return;
  184.         }
  185.         if (\strpos($requestBase, "/_profiler") === 0) {
  186.             return;
  187.         }
  188.         if ($queryString !== '') {
  189.             $queryString = urldecode($queryString);
  190.             $requestUri .= '?' . $queryString;
  191.         }
  192.         // try to load the seo route
  193.         $context = Context::createDefaultContext();
  194.         $redirects = $this->seoUrlRepository->search((new Criteria())
  195.             ->addFilter(new EqualsAnyFilter('pathInfo', [$requestUri])), $context);
  196.         // if found overwrite search term with the seo route
  197.         if ($redirects->count() !== 0) {
  198.             foreach ($redirects as $redirect) {
  199.                 $requestBase = $redirect->getSeoPathInfo();
  200.                 // Search for Redirect
  201.                 $search[] = [
  202.                     $requestBaseUrl . '/' . $requestBase, // relative url with shopware 6 in sub folder: /public/Ergonomic-Concrete-Cough-Machine/48314803f1244f609a2ce907bfb48f53
  203.                     $requestBaseUrl . $requestBase, // relative url with shopware 6 in sub folder url is not shopware seo url: /public/test
  204.                     $storefrontUri . $requestBase, // absolute url with shopware 6 in sub folder, full url with host: http://shopware-platform.local/public/test1
  205.                     $storefrontUri . '/' . $requestBase, // absolute url with shopware 6 in sub folder, full url with host and slash at the end: http://shopware-platform.local/public/Freizeit-Elektro/Telefone/
  206.                     $requestBase, // relative url domain configured in public folder: /Ergonomic-Concrete-Cough-Machine/48314803f1244f609a2ce907bfb48f53 or /test4
  207.                     '/' . $requestBase, // absolute url domain configured in public folder: http://shopware-platform.local/Shoes-Baby/
  208.                     \substr($requestBase, 1), // e.g. "test"
  209.                 ];
  210.             }
  211.         }
  212.         if (!empty($search)) {
  213.             $search = array_merge(...$search);
  214.         } else {
  215.             $search = [
  216.                 $requestBaseUrl . '/' . $requestUri, // relative url with shopware 6 in sub folder: /public/Ergonomic-Concrete-Cough-Machine/48314803f1244f609a2ce907bfb48f53
  217.                 $requestBaseUrl . $requestUri, // relative url with shopware 6 in sub folder url is not shopware seo url: /public/test
  218.                 $storefrontUri . $requestUri, // absolute url with shopware 6 in sub folder, full url with host: http://shopware-platform.local/public/test1
  219.                 $storefrontUri . '/' . $requestUri, // absolute url with shopware 6 in sub folder, full url with host and slash at the end: http://shopware-platform.local/public/Freizeit-Elektro/Telefone/
  220.                 $requestUri, // relative url domain configured in public folder: /Ergonomic-Concrete-Cough-Machine/48314803f1244f609a2ce907bfb48f53 or /test4
  221.                 '/' . $requestUri, // absolute url domain configured in public folder: http://shopware-platform.local/Shoes-Baby/
  222.                 \substr($requestUri, 1), // e.g. "test"
  223.             ];
  224.         }
  225.         // search for the redirect in the database
  226.         $redirects = $this->repository->search((new Criteria())->addFilter(new EqualsAnyFilter('sourceURL', $search))->addFilter(new EqualsFilter('enabled', true))
  227.             ->setLimit(1), $context);
  228.         if ($redirects->count() === 0) {
  229.             // Checks if the requested URL contains Query parameters, and if so, checks if a redirect can be found with the ignoreQueryParams option
  230.             if (str_contains($requestUri, '?')) {
  231.                 $searchWithoutQuery = [];
  232.                 foreach ($search as $string)
  233.                     $searchWithoutQuery[] = explode('?', $string)[0];
  234.                 $redirects = $this->repository->search((new Criteria())->addFilter(new EqualsAnyFilter('sourceURL', $searchWithoutQuery))->addFilter(new EqualsFilter('enabled', true))->addFilter(new EqualsFilter('ignoreQueryParams', true))
  235.                     ->setLimit(1), $context);
  236.                 // No Redirect found for this URL, do nothing
  237.                 if ($redirects->count() === 0) {
  238.                     return;
  239.                 }
  240.             } else {
  241.                 // No Redirect found for this URL, do nothing
  242.                 return;
  243.             }
  244.         }
  245.         $redirect = $redirects->first();
  246.         $targetURL = $redirect->getTargetURL();
  247.         $code = $redirect->getHttpCode();
  248.         // Prevent endless redirecting when target url belongs to the same seo url like the source url or when target url and source url have only different capitalisation
  249.         if (in_array($targetURL, $search, true)) {
  250.             return;
  251.         }
  252.         /*
  253.          *  checks if $targetURL is a full url or path and redirects accordingly
  254.          */
  255.         if (!(\strpos($targetURL, "http:") === 0 || \strpos($targetURL, "https:") === 0)) {
  256.             if (\strpos($targetURL, "www.") === 0) {
  257.                 $targetURL = "http://" . $targetURL;
  258.             } else {
  259.                 if (\strpos($targetURL, "/") !== 0) {
  260.                     $targetURL = "/" . $targetURL;
  261.                 }
  262.             }
  263.         }
  264.         $event->setResponse(new RedirectResponse($targetURL, $code));
  265.     }
  266. }