vendor/contao/core-bundle/src/Routing/Page/PageRegistry.php line 55

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\Routing\Page;
  11. use Contao\PageModel;
  12. use Doctrine\DBAL\Connection;
  13. use Symfony\Contracts\Service\ResetInterface;
  14. class PageRegistry implements ResetInterface
  15. {
  16.     private const DISABLE_CONTENT_COMPOSITION = ['redirect''forward''logout'];
  17.     private Connection $connection;
  18.     private ?array $urlPrefixes null;
  19.     private ?array $urlSuffixes null;
  20.     /**
  21.      * @var array<string,RouteConfig>
  22.      */
  23.     private array $routeConfigs = [];
  24.     /**
  25.      * @var array<string,DynamicRouteInterface>
  26.      */
  27.     private array $routeEnhancers = [];
  28.     /**
  29.      * @var array<string,ContentCompositionInterface|bool>
  30.      */
  31.     private array $contentComposition = [];
  32.     public function __construct(Connection $connection)
  33.     {
  34.         $this->connection $connection;
  35.     }
  36.     /**
  37.      * Returns the route for a page.
  38.      *
  39.      * If no path is configured (is null), the route will accept
  40.      * any parameters after the page alias (e.g. "en/page-alias/foo/bar.html").
  41.      *
  42.      * A route enhancer might enhance the route for a specific page.
  43.      */
  44.     public function getRoute(PageModel $pageModel): PageRoute
  45.     {
  46.         $type $pageModel->type;
  47.         $config $this->routeConfigs[$type] ?? new RouteConfig();
  48.         $defaults $config->getDefaults();
  49.         $requirements $config->getRequirements();
  50.         $options $config->getOptions();
  51.         $path $config->getPath();
  52.         if (false === $path) {
  53.             $path '';
  54.             $options['compiler_class'] = UnroutablePageRouteCompiler::class;
  55.         } elseif (null === $path) {
  56.             if ($this->isParameterless($pageModel)) {
  57.                 $path '/'.($pageModel->alias ?: $pageModel->id);
  58.             } else {
  59.                 $path '/'.($pageModel->alias ?: $pageModel->id).'{!parameters}';
  60.                 $defaults['parameters'] = '';
  61.                 $requirements['parameters'] = $pageModel->requireItem '/.+?' '(/.+?)?';
  62.             }
  63.         }
  64.         $route = new PageRoute($pageModel$path$defaults$requirements$options$config->getMethods());
  65.         if (null !== $config->getUrlSuffix()) {
  66.             $route->setUrlSuffix($config->getUrlSuffix());
  67.         }
  68.         if (!isset($this->routeEnhancers[$type])) {
  69.             return $route;
  70.         }
  71.         /** @var DynamicRouteInterface $enhancer */
  72.         $enhancer $this->routeEnhancers[$type];
  73.         $enhancer->configurePageRoute($route);
  74.         return $route;
  75.     }
  76.     public function getPathRegex(): array
  77.     {
  78.         $prefixes = [];
  79.         foreach ($this->routeConfigs as $type => $config) {
  80.             $regex $config->getPathRegex();
  81.             if (null !== $regex) {
  82.                 $prefixes[$type] = $regex;
  83.             }
  84.         }
  85.         return $prefixes;
  86.     }
  87.     public function supportsContentComposition(PageModel $pageModel): bool
  88.     {
  89.         if (!isset($this->contentComposition[$pageModel->type])) {
  90.             return !\in_array($pageModel->typeself::DISABLE_CONTENT_COMPOSITIONtrue);
  91.         }
  92.         $service $this->contentComposition[$pageModel->type];
  93.         if ($service instanceof ContentCompositionInterface) {
  94.             return $service->supportsContentComposition($pageModel);
  95.         }
  96.         return (bool) $service;
  97.     }
  98.     /**
  99.      * @return array<string>
  100.      */
  101.     public function getUrlPrefixes(): array
  102.     {
  103.         $this->initializePrefixAndSuffix();
  104.         return $this->urlPrefixes;
  105.     }
  106.     /**
  107.      * @return array<string>
  108.      */
  109.     public function getUrlSuffixes(): array
  110.     {
  111.         $this->initializePrefixAndSuffix();
  112.         return $this->urlSuffixes;
  113.     }
  114.     /**
  115.      * @param ContentCompositionInterface|bool $contentComposition
  116.      */
  117.     public function add(string $typeRouteConfig $config, ?DynamicRouteInterface $routeEnhancer null$contentComposition true): self
  118.     {
  119.         // Override existing pages with the same identifier
  120.         $this->routeConfigs[$type] = $config;
  121.         if ($routeEnhancer) {
  122.             $this->routeEnhancers[$type] = $routeEnhancer;
  123.         }
  124.         $this->contentComposition[$type] = $contentComposition;
  125.         // Make sure to reset caches when a page type is added
  126.         $this->urlPrefixes null;
  127.         $this->urlSuffixes null;
  128.         return $this;
  129.     }
  130.     public function remove(string $type): self
  131.     {
  132.         unset(
  133.             $this->routeConfigs[$type],
  134.             $this->routeEnhancers[$type],
  135.             $this->contentComposition[$type]
  136.         );
  137.         $this->urlPrefixes $this->urlSuffixes null;
  138.         return $this;
  139.     }
  140.     public function keys(): array
  141.     {
  142.         return array_keys($this->routeConfigs);
  143.     }
  144.     /**
  145.      * Checks whether this is a routable page type (see #3415).
  146.      */
  147.     public function isRoutable(PageModel $page): bool
  148.     {
  149.         $type $page->type;
  150.         // Any legacy page without route config is routable by default
  151.         if (!isset($this->routeConfigs[$type])) {
  152.             return true;
  153.         }
  154.         // Check if page controller is routable
  155.         return false !== $this->routeConfigs[$type]->getPath();
  156.     }
  157.     /**
  158.      * @return array<string>
  159.      */
  160.     public function getUnroutableTypes(): array
  161.     {
  162.         $types = [];
  163.         foreach ($this->routeConfigs as $type => $config) {
  164.             if (false === $config->getPath()) {
  165.                 $types[] = $type;
  166.             }
  167.         }
  168.         return $types;
  169.     }
  170.     public function reset(): void
  171.     {
  172.         $this->urlPrefixes null;
  173.         $this->urlSuffixes null;
  174.     }
  175.     private function initializePrefixAndSuffix(): void
  176.     {
  177.         if (null !== $this->urlPrefixes || null !== $this->urlSuffixes) {
  178.             return;
  179.         }
  180.         $results $this->connection->fetchAllAssociative("SELECT urlPrefix, urlSuffix FROM tl_page WHERE type='root'");
  181.         $urlSuffixes = [
  182.             array_column($results'urlSuffix'),
  183.             array_filter(array_map(
  184.                 static fn (RouteConfig $config) => $config->getUrlSuffix(),
  185.                 $this->routeConfigs,
  186.             )),
  187.         ];
  188.         foreach ($this->routeConfigs as $config) {
  189.             if (null !== ($suffix $config->getUrlSuffix())) {
  190.                 $urlSuffixes[] = [$suffix];
  191.             }
  192.         }
  193.         foreach ($this->routeEnhancers as $enhancer) {
  194.             $urlSuffixes[] = $enhancer->getUrlSuffixes();
  195.         }
  196.         $this->urlSuffixes array_values(array_unique(array_merge(...$urlSuffixes)));
  197.         $this->urlPrefixes array_values(array_unique(array_column($results'urlPrefix')));
  198.     }
  199.     private function isParameterless(PageModel $pageModel): bool
  200.     {
  201.         if ('redirect' === $pageModel->type) {
  202.             return true;
  203.         }
  204.         return 'forward' === $pageModel->type && !$pageModel->alwaysForward;
  205.     }
  206. }