vendor/contao/core-bundle/src/Routing/Matcher/LegacyMatcher.php line 45

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\Matcher;
  11. use Contao\Config;
  12. use Contao\CoreBundle\Framework\ContaoFramework;
  13. use Contao\CoreBundle\Util\LocaleUtil;
  14. use Contao\Input;
  15. use Contao\PageModel;
  16. use Contao\System;
  17. use Symfony\Component\HttpFoundation\Request;
  18. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  19. use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
  20. class LegacyMatcher implements RequestMatcherInterface
  21. {
  22.     private ContaoFramework $framework;
  23.     private RequestMatcherInterface $requestMatcher;
  24.     private string $urlSuffix;
  25.     private bool $prependLocale;
  26.     /**
  27.      * @internal Do not inherit from this class; decorate the "contao.routing.legacy_matcher" service instead
  28.      */
  29.     public function __construct(ContaoFramework $frameworkRequestMatcherInterface $requestMatcherstring $urlSuffixbool $prependLocale)
  30.     {
  31.         $this->framework $framework;
  32.         $this->requestMatcher $requestMatcher;
  33.         $this->urlSuffix $urlSuffix;
  34.         $this->prependLocale $prependLocale;
  35.     }
  36.     public function matchRequest(Request $request): array
  37.     {
  38.         $this->framework->initialize(true);
  39.         $pathInfo rawurldecode($request->getPathInfo());
  40.         if (
  41.             '/' === $pathInfo
  42.             || empty($GLOBALS['TL_HOOKS']['getPageIdFromUrl'])
  43.             || !\is_array($GLOBALS['TL_HOOKS']['getPageIdFromUrl'])
  44.             || ($this->prependLocale && preg_match('@^/([a-z]{2}(-[A-Z]{2})?)/$@'$pathInfo))
  45.         ) {
  46.             return $this->requestMatcher->matchRequest($request);
  47.         }
  48.         $locale null;
  49.         $fragments null;
  50.         try {
  51.             $match $this->requestMatcher->matchRequest($request);
  52.             $fragments $this->createFragmentsFromMatch($match);
  53.             $locale = isset($match['_locale']) ? LocaleUtil::formatAsLanguageTag($match['_locale']) : null;
  54.         } catch (ResourceNotFoundException $e) {
  55.             // continue and parse fragments from path
  56.         }
  57.         if (null === $fragments) {
  58.             $pathInfo $this->parseSuffixAndLanguage($pathInfo$locale);
  59.             $fragments $this->createFragmentsFromPath($pathInfo);
  60.         }
  61.         if ($this->prependLocale) {
  62.             if (null === $locale) {
  63.                 throw new ResourceNotFoundException('Locale is missing');
  64.             }
  65.             $input $this->framework->getAdapter(Input::class);
  66.             $input->setGet('language'$locale);
  67.         }
  68.         trigger_deprecation('contao/core-bundle''4.0''Using the "getPageIdFromUrl" hook has been deprecated and will no longer work in Contao 5.0.');
  69.         $fragments $this->executeLegacyHook($fragments);
  70.         $pathInfo $this->createPathFromFragments($fragments$locale);
  71.         return $this->requestMatcher->matchRequest($this->rebuildRequest($pathInfo$request));
  72.     }
  73.     private function createFragmentsFromMatch(array $match): array
  74.     {
  75.         $page $match['pageModel'] ?? null;
  76.         $parameters $match['parameters'] ?? '';
  77.         if (!$page instanceof PageModel) {
  78.             throw new ResourceNotFoundException('Resource not found');
  79.         }
  80.         if ('' === $parameters) {
  81.             return [$page->alias];
  82.         }
  83.         $config $this->framework->getAdapter(Config::class);
  84.         $fragments = [...[$page->alias], ...explode('/'substr($parameters1))];
  85.         // Add the second fragment as auto_item if the number of fragments is even
  86.         if ($config->get('useAutoItem') && === \count($fragments) % 2) {
  87.             array_splice($fragments10, ['auto_item']);
  88.         }
  89.         return $fragments;
  90.     }
  91.     private function createFragmentsFromPath(string $pathInfo): array
  92.     {
  93.         $config $this->framework->getAdapter(Config::class);
  94.         $fragments explode('/'$pathInfo);
  95.         // Add the second fragment as auto_item if the number of fragments is even
  96.         if ($config->get('useAutoItem') && === \count($fragments) % 2) {
  97.             array_splice($fragments10, ['auto_item']);
  98.         }
  99.         return $fragments;
  100.     }
  101.     private function executeLegacyHook(array $fragments): array
  102.     {
  103.         $system $this->framework->getAdapter(System::class);
  104.         foreach ($GLOBALS['TL_HOOKS']['getPageIdFromUrl'] as $callback) {
  105.             $fragments $system->importStatic($callback[0])->{$callback[1]}($fragments);
  106.         }
  107.         // Return if the alias is empty (see #4702 and #4972)
  108.         if ('' === $fragments[0]) {
  109.             throw new ResourceNotFoundException('Page alias is empty');
  110.         }
  111.         return $fragments;
  112.     }
  113.     private function createPathFromFragments(array $fragments, ?string $locale): string
  114.     {
  115.         $config $this->framework->getAdapter(Config::class);
  116.         if (isset($fragments[1]) && 'auto_item' === $fragments[1] && $config->get('useAutoItem')) {
  117.             unset($fragments[1]);
  118.         }
  119.         $pathInfo implode('/'$fragments).$this->urlSuffix;
  120.         if ($this->prependLocale) {
  121.             $pathInfo $locale.'/'.$pathInfo;
  122.         }
  123.         return '/'.$pathInfo;
  124.     }
  125.     private function parseSuffixAndLanguage(string $pathInfo, ?string &$locale): string
  126.     {
  127.         $suffixLength = \strlen($this->urlSuffix);
  128.         if (!== $suffixLength) {
  129.             if (substr($pathInfo, -$suffixLength) !== $this->urlSuffix) {
  130.                 throw new ResourceNotFoundException('URL suffix does not match');
  131.             }
  132.             $pathInfo substr($pathInfo0, -$suffixLength);
  133.         }
  134.         if (=== strncmp($pathInfo'/'1)) {
  135.             $pathInfo substr($pathInfo1);
  136.         }
  137.         if ($this->prependLocale) {
  138.             $matches = [];
  139.             if (!preg_match('@^([a-z]{2}(-[A-Z]{2})?)/(.+)$@'$pathInfo$matches)) {
  140.                 throw new ResourceNotFoundException('Locale does not match');
  141.             }
  142.             [, $locale,, $pathInfo] = $matches;
  143.         }
  144.         return $pathInfo;
  145.     }
  146.     /**
  147.      * @see ChainRouter::rebuildRequest()
  148.      */
  149.     private function rebuildRequest(string $pathinfoRequest $request): Request
  150.     {
  151.         $uri $pathinfo;
  152.         $server = [];
  153.         if ($request->getBaseUrl()) {
  154.             $uri $request->getBaseUrl().$pathinfo;
  155.             $server['SCRIPT_FILENAME'] = $request->getBaseUrl();
  156.             $server['PHP_SELF'] = $request->getBaseUrl();
  157.         }
  158.         $host $request->getHttpHost() ?: 'localhost';
  159.         $scheme $request->getScheme() ?: 'http';
  160.         $uri $scheme.'://'.$host.$uri.'?'.$request->getQueryString();
  161.         return Request::create($uri$request->getMethod(), [], [], [], $server);
  162.     }
  163. }