vendor/sulu/sulu/src/Sulu/Bundle/RouteBundle/Routing/RouteProvider.php line 125

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\RouteBundle\Routing;
  11. use PHPCR\Util\PathHelper;
  12. use ProxyManager\Factory\LazyLoadingValueHolderFactory;
  13. use ProxyManager\Proxy\LazyLoadingInterface;
  14. use Sulu\Bundle\RouteBundle\Entity\Route as SuluRoute;
  15. use Sulu\Bundle\RouteBundle\Entity\RouteRepositoryInterface;
  16. use Sulu\Bundle\RouteBundle\Model\RouteInterface;
  17. use Sulu\Bundle\RouteBundle\Routing\Defaults\RouteDefaultsProviderInterface;
  18. use Sulu\Component\Content\Document\Behavior\ExtensionBehavior;
  19. use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes;
  20. use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
  21. use Symfony\Cmf\Component\Routing\RouteProviderInterface;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\RequestStack;
  24. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  25. use Symfony\Component\Routing\Route;
  26. use Symfony\Component\Routing\RouteCollection;
  27. /**
  28.  * Provides symfony-routes by request or name.
  29.  */
  30. class RouteProvider implements RouteProviderInterface
  31. {
  32.     public const ROUTE_PREFIX 'sulu_route_';
  33.     /**
  34.      * @var RouteRepositoryInterface
  35.      */
  36.     private $routeRepository;
  37.     /**
  38.      * @var RequestAnalyzerInterface
  39.      */
  40.     private $requestAnalyzer;
  41.     /**
  42.      * @var RouteDefaultsProviderInterface
  43.      */
  44.     private $routeDefaultsProvider;
  45.     /**
  46.      * @var RequestStack
  47.      */
  48.     private $requestStack;
  49.     /**
  50.      * @var LazyLoadingValueHolderFactory
  51.      */
  52.     private $proxyFactory;
  53.     /**
  54.      * @var array
  55.      */
  56.     private $defaultOptions;
  57.     /**
  58.      * @var Route[]
  59.      */
  60.     private $symfonyRouteCache = [];
  61.     /**
  62.      * @var SuluRoute[]
  63.      */
  64.     private $routeCache = [];
  65.     public function __construct(
  66.         RouteRepositoryInterface $routeRepository,
  67.         RequestAnalyzerInterface $requestAnalyzer,
  68.         RouteDefaultsProviderInterface $routeDefaultsProvider,
  69.         RequestStack $requestStack,
  70.         LazyLoadingValueHolderFactory $proxyFactory null,
  71.         array $defaultOptions = []
  72.     ) {
  73.         $this->routeRepository $routeRepository;
  74.         $this->requestAnalyzer $requestAnalyzer;
  75.         $this->routeDefaultsProvider $routeDefaultsProvider;
  76.         $this->requestStack $requestStack;
  77.         $this->proxyFactory $proxyFactory ?: new LazyLoadingValueHolderFactory();
  78.         $this->defaultOptions $defaultOptions;
  79.     }
  80.     public function getRouteCollectionForRequest(Request $request)
  81.     {
  82.         $collection = new RouteCollection();
  83.         $path $this->decodePathInfo($request->getPathInfo());
  84.         /** @var RequestAttributes|null $attributes */
  85.         $attributes $request->attributes->get('_sulu');
  86.         if (!$attributes) {
  87.             return $collection;
  88.         }
  89.         $matchType $attributes->getAttribute('matchType');
  90.         if (RequestAnalyzerInterface::MATCH_TYPE_REDIRECT == $matchType
  91.             || RequestAnalyzerInterface::MATCH_TYPE_PARTIAL == $matchType
  92.         ) {
  93.             return $collection;
  94.         }
  95.         $prefix $attributes->getAttribute('resourceLocatorPrefix');
  96.         if (!empty($prefix) && === \strpos($path$prefix)) {
  97.             $path PathHelper::relativizePath($path$prefix);
  98.         }
  99.         // when the URI ends with a dot - symfony returns empty request-format
  100.         if ('' === $format $request->getRequestFormat()) {
  101.             return $collection;
  102.         }
  103.         $path $this->stripFormatExtension($path$format);
  104.         $route $this->findRouteByPath($path$request->getLocale());
  105.         if ($route && \array_key_exists($route->getId(), $this->symfonyRouteCache)) {
  106.             $collection->add(
  107.                 self::ROUTE_PREFIX $route->getId(),
  108.                 $this->symfonyRouteCache[$route->getId()]
  109.             );
  110.             return $collection;
  111.         }
  112.         if (!$route
  113.             || !$this->routeDefaultsProvider->supports($route->getEntityClass())
  114.             || !$this->routeDefaultsProvider->isPublished(
  115.                 $route->getEntityClass(),
  116.                 $route->getEntityId(),
  117.                 $route->getLocale()
  118.             )
  119.         ) {
  120.             return $collection;
  121.         }
  122.         $symfonyRoute $this->createRoute($route$request$attributes);
  123.         $routeObject $symfonyRoute->getDefaults()['object'] ?? null;
  124.         if ($routeObject instanceof ExtensionBehavior) {
  125.             $portal $attributes->getAttribute('portal');
  126.             $documentSegments $routeObject->getExtensionsData()['excerpt']['segments'] ?? [];
  127.             $documentSegmentKey $documentSegments[$portal->getWebspace()->getKey()] ?? null;
  128.             $segment $this->requestAnalyzer->getSegment();
  129.             if ($segment && $documentSegmentKey && $segment->getKey() !== $documentSegmentKey) {
  130.                 $this->requestAnalyzer->changeSegment($documentSegmentKey);
  131.             }
  132.         }
  133.         $collection->add(self::ROUTE_PREFIX $route->getId(), $symfonyRoute);
  134.         return $collection;
  135.     }
  136.     /**
  137.      * Find route and cache it.
  138.      *
  139.      * @param string $path
  140.      * @param string $locale
  141.      *
  142.      * @return SuluRoute
  143.      */
  144.     private function findRouteByPath($path$locale)
  145.     {
  146.         $path '/' \ltrim($path'/');
  147.         if (!\array_key_exists($path$this->routeCache)) {
  148.             $this->routeCache[$path] = $this->routeRepository->findByPath($path$locale);
  149.         }
  150.         return $this->routeCache[$path];
  151.     }
  152.     public function getRouteByName($name)
  153.     {
  154.         if (!== \strpos($nameself::ROUTE_PREFIX)) {
  155.             throw new RouteNotFoundException();
  156.         }
  157.         $request $this->requestStack->getCurrentRequest();
  158.         /** @var RequestAttributes $attributes */
  159.         $attributes $request->attributes->get('_sulu');
  160.         if (!$attributes) {
  161.             throw new RouteNotFoundException();
  162.         }
  163.         $routeId \substr($name\strlen(self::ROUTE_PREFIX));
  164.         if (\array_key_exists($routeId$this->symfonyRouteCache)) {
  165.             return $this->symfonyRouteCache[$routeId];
  166.         }
  167.         /** @var RouteInterface $route */
  168.         $route $this->routeRepository->find($routeId);
  169.         if (!$route
  170.             || !$this->routeDefaultsProvider->supports($route->getEntityClass())
  171.             || !$this->routeDefaultsProvider->isPublished(
  172.                 $route->getEntityClass(),
  173.                 $route->getEntityId(),
  174.                 $route->getLocale()
  175.             )
  176.         ) {
  177.             throw new RouteNotFoundException();
  178.         }
  179.         return $this->createRoute($route$request$attributes);
  180.     }
  181.     public function getRoutesByNames($names)
  182.     {
  183.         return [];
  184.     }
  185.     /**
  186.      * Will create a symfony route.
  187.      *
  188.      * @return Route
  189.      */
  190.     protected function createRoute(RouteInterface $routeRequest $requestRequestAttributes $attributes)
  191.     {
  192.         $routePath $this->decodePathInfo($request->getPathInfo());
  193.         $target $route->getTarget();
  194.         if ($route->isHistory() && $target) {
  195.             return new Route(
  196.                 $routePath,
  197.                 [
  198.                     '_controller' => 'sulu_website.redirect_controller::redirectAction',
  199.                     'url' => $request->getSchemeAndHttpHost()
  200.                         . $attributes->getAttribute('resourceLocatorPrefix')
  201.                         . $target->getPath()
  202.                         . ($request->getQueryString() ? ('?' $request->getQueryString()) : ''),
  203.                 ],
  204.                 [],
  205.                 $this->defaultOptions
  206.             );
  207.         }
  208.         $symfonyRoute $this->proxyFactory->createProxy(
  209.             Route::class,
  210.             function(&$wrappedObjectLazyLoadingInterface $proxy$method, array $parameters, &$initializer) use (
  211.                 $routePath,
  212.                 $route,
  213.                 $request
  214.             ) {
  215.                 $initializer null// disable initialization
  216.                 $wrappedObject = new Route(
  217.                     $routePath,
  218.                     $this->routeDefaultsProvider->getByEntity(
  219.                         $route->getEntityClass(),
  220.                         $route->getEntityId(),
  221.                         $request->getLocale()
  222.                     ),
  223.                     [],
  224.                     $this->defaultOptions
  225.                 );
  226.                 return true;
  227.             }
  228.         );
  229.         return $this->symfonyRouteCache[$route->getId()] = $symfonyRoute;
  230.     }
  231.     /**
  232.      * Server encodes the url and symfony does not encode it
  233.      * Symfony decodes this data here https://github.com/symfony/symfony/blob/3.3/src/Symfony/Component/Routing/Matcher/UrlMatcher.php#L91.
  234.      *
  235.      * @param $pathInfo
  236.      *
  237.      * @return string
  238.      */
  239.     private function decodePathInfo($pathInfo)
  240.     {
  241.         if (null === $pathInfo || '' === $pathInfo) {
  242.             return '';
  243.         }
  244.         return '/' \ltrim(\rawurldecode($pathInfo), '/');
  245.     }
  246.     /**
  247.      * Return the given path without the format extension.
  248.      *
  249.      * @param string $path
  250.      * @param string $format
  251.      *
  252.      * @return string
  253.      */
  254.     private function stripFormatExtension($path$format)
  255.     {
  256.         $extension '.' $format;
  257.         if (\substr($path, -\strlen($extension)) === $extension) {
  258.             $path \substr($path0\strlen($path) - \strlen($extension));
  259.         }
  260.         return $path;
  261.     }
  262. }