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

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