vendor/sulu/sulu/src/Sulu/Bundle/WebsiteBundle/EventListener/RedirectExceptionSubscriber.php line 78

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Sulu.
  4.  *
  5.  * (c) Sulu GmbH
  6.  *
  7.  * This source file is subject to the MIT license that is bundled
  8.  * with this source code in the file LICENSE.
  9.  */
  10. namespace Sulu\Bundle\WebsiteBundle\EventListener;
  11. use Sulu\Bundle\WebsiteBundle\Locale\DefaultLocaleProviderInterface;
  12. use Sulu\Component\Localization\Localization;
  13. use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes;
  14. use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
  15. use Sulu\Component\Webspace\Url\ReplacerInterface;
  16. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  17. use Symfony\Component\HttpFoundation\RedirectResponse;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  20. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  21. use Symfony\Component\HttpKernel\KernelEvents;
  22. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  23. use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
  24. /**
  25.  * This event-listener redirect trailing slashes and ".html" and redirects to default locale for partial-matches.
  26.  */
  27. class RedirectExceptionSubscriber implements EventSubscriberInterface
  28. {
  29.     /**
  30.      * @var RequestMatcherInterface
  31.      */
  32.     private $router;
  33.     /**
  34.      * @var RequestAnalyzerInterface
  35.      */
  36.     private $requestAnalyzer;
  37.     /**
  38.      * @var DefaultLocaleProviderInterface
  39.      */
  40.     private $defaultLocaleProvider;
  41.     /**
  42.      * @var ReplacerInterface
  43.      */
  44.     private $urlReplacer;
  45.     public function __construct(
  46.         RequestMatcherInterface $router,
  47.         RequestAnalyzerInterface $requestAnalyzer,
  48.         DefaultLocaleProviderInterface $defaultLocaleProvider,
  49.         ReplacerInterface $urlReplacer
  50.     ) {
  51.         $this->router $router;
  52.         $this->requestAnalyzer $requestAnalyzer;
  53.         $this->defaultLocaleProvider $defaultLocaleProvider;
  54.         $this->urlReplacer $urlReplacer;
  55.     }
  56.     public static function getSubscribedEvents()
  57.     {
  58.         return [
  59.             KernelEvents::EXCEPTION => [
  60.                 ['redirectPartialMatch'0],
  61.                 ['redirectTrailingSlashOrHtml'0],
  62.             ],
  63.         ];
  64.     }
  65.     /**
  66.      * Redirect trailing slashes or ".html".
  67.      */
  68.     public function redirectTrailingSlashOrHtml(ExceptionEvent $event)
  69.     {
  70.         if (!$event->getThrowable() instanceof NotFoundHttpException) {
  71.             return;
  72.         }
  73.         $request $event->getRequest();
  74.         /** @var RequestAttributes $attributes */
  75.         $attributes $request->attributes->get('_sulu');
  76.         if (!$attributes) {
  77.             return;
  78.         }
  79.         $prefix $attributes->getAttribute('resourceLocatorPrefix');
  80.         $resourceLocator $attributes->getAttribute('resourceLocator');
  81.         $route '/' \trim($prefix $resourceLocator'/');
  82.         if (!\in_array($request->getRequestFormat(), ['htm''html'])
  83.             || $route === $request->getPathInfo()
  84.             || !$this->matchRoute($request->getSchemeAndHttpHost() . $route)
  85.         ) {
  86.             return;
  87.         }
  88.         $event->setResponse(new RedirectResponse($route301));
  89.     }
  90.     /**
  91.      * Redirect partial and redirect matches.
  92.      */
  93.     public function redirectPartialMatch(ExceptionEvent $event)
  94.     {
  95.         if (!$event->getThrowable() instanceof NotFoundHttpException) {
  96.             return;
  97.         }
  98.         $request $event->getRequest();
  99.         /** @var RequestAttributes $attributes */
  100.         $attributes $event->getRequest()->attributes->get('_sulu');
  101.         if (!$attributes) {
  102.             return;
  103.         }
  104.         $types = [RequestAnalyzerInterface::MATCH_TYPE_REDIRECTRequestAnalyzerInterface::MATCH_TYPE_PARTIAL];
  105.         $matchType $attributes->getAttribute('matchType');
  106.         if (!\in_array($matchType$types)) {
  107.             return;
  108.         }
  109.         $localization $this->defaultLocaleProvider->getDefaultLocale();
  110.         $redirect $attributes->getAttribute('redirect');
  111.         $redirect $this->urlReplacer->replaceCountry($redirect$localization->getCountry());
  112.         $redirect $this->urlReplacer->replaceLanguage($redirect$localization->getLanguage());
  113.         $redirect $this->urlReplacer->replaceLocalization($redirect$localization->getLocale(Localization::DASH));
  114.         $route $this->resolveRedirectUrl(
  115.             $redirect,
  116.             $request->getUri(),
  117.             $attributes->getAttribute('resourceLocatorPrefix')
  118.         );
  119.         if (!$this->matchRoute($route)) {
  120.             return;
  121.         }
  122.         $event->setResponse(new RedirectResponse($route301));
  123.     }
  124.     /**
  125.      * Returns true if given route exists.
  126.      *
  127.      * @param string $route
  128.      *
  129.      * @return bool
  130.      */
  131.     private function matchRoute($route)
  132.     {
  133.         return $this->matchUrl($route);
  134.     }
  135.     /**
  136.      * Returns true if given url exists.
  137.      *
  138.      * @param string $url
  139.      *
  140.      * @return bool
  141.      */
  142.     private function matchUrl($url)
  143.     {
  144.         $request Request::create($url);
  145.         $this->requestAnalyzer->analyze($request);
  146.         try {
  147.             return null !== $this->router->matchRequest($request);
  148.         } catch (ResourceNotFoundException $exception) {
  149.             return false;
  150.         }
  151.     }
  152.     /**
  153.      * Resolve the redirect URL, appending any additional path data.
  154.      *
  155.      * @param string $redirectUrl Redirect webspace URI
  156.      * @param string $requestUri The actual incoming request URI
  157.      * @param string $resourceLocatorPrefix The prefix of the actual portal
  158.      *
  159.      * @return string URL to redirect to
  160.      */
  161.     private function resolveRedirectUrl($redirectUrl$requestUri$resourceLocatorPrefix)
  162.     {
  163.         $redirectInfo $this->parseUrl($redirectUrl);
  164.         $requestInfo $this->parseUrl($requestUri);
  165.         $url \sprintf('%s://%s'$requestInfo['scheme'], $requestInfo['host']);
  166.         if (isset($redirectInfo['host'])) {
  167.             $url \sprintf('%s://%s'$requestInfo['scheme'], $redirectInfo['host']);
  168.         }
  169.         if (isset($requestInfo['port'])) {
  170.             $url .= ':' $requestInfo['port'];
  171.         }
  172.         if (isset($redirectInfo['path'])
  173.             && (// if requested url not starting with redirectUrl it need to be added
  174.                 !isset($requestInfo['path'])
  175.                 || !== \strpos($requestInfo['path'], $redirectInfo['path'] . '/'))
  176.         ) {
  177.             $url .= $redirectInfo['path'];
  178.         }
  179.         if (isset($requestInfo['path']) && $resourceLocatorPrefix !== $requestInfo['path']) {
  180.             $path $requestInfo['path'];
  181.             if ($resourceLocatorPrefix && === \strpos($path$resourceLocatorPrefix)) {
  182.                 $path \substr($path\strlen($resourceLocatorPrefix));
  183.             }
  184.             [$resourceLocator$formatResult] = $this->getResourceLocatorFromRequest($path);
  185.             $url .= $resourceLocator;
  186.             $url \rtrim($url'/') . (null !== $formatResult ? ('.' $formatResult) : '');
  187.         }
  188.         if (isset($requestInfo['query'])) {
  189.             $url .= '?' $requestInfo['query'];
  190.         }
  191.         if (isset($requestInfo['fragment'])) {
  192.             $url .= '#' $requestInfo['fragment'];
  193.         }
  194.         return $url;
  195.     }
  196.     /**
  197.      * Prefix http to the URL if it is missing and
  198.      * then parse the string using parse_url.
  199.      *
  200.      * @param string $url
  201.      *
  202.      * @return array
  203.      */
  204.     private function parseUrl($url)
  205.     {
  206.         if (!\preg_match('{^https?://}'$url)) {
  207.             $url 'http://' $url;
  208.         }
  209.         return \parse_url($url);
  210.     }
  211.     /**
  212.      * @return array<int, string|null>
  213.      */
  214.     private function getResourceLocatorFromRequest(string $path): array
  215.     {
  216.         // extract file and extension info
  217.         $pathParts \explode('/'$path);
  218.         $fileInfo \explode('.'\array_pop($pathParts));
  219.         $resourceLocator \rtrim(\implode('/'$pathParts), '/') . '/' $fileInfo[0];
  220.         $formatResult null;
  221.         if (\count($fileInfo) > 1) {
  222.             $formatResult \end($fileInfo);
  223.         }
  224.         return [$resourceLocator$formatResult];
  225.     }
  226. }