‪TYPO3CMS  9.5
SiteMatcher.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
3 
5 
6 /*
7  * This file is part of the TYPO3 CMS project.
8  *
9  * It is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License, either version 2
11  * of the License, or any later version.
12  *
13  * For the full copyright and license information, please read the
14  * LICENSE.txt file that was distributed with this source code.
15  *
16  * The TYPO3 project - inspiring people to share!
17  */
18 
19 use Psr\Http\Message\ServerRequestInterface;
20 use Symfony\Component\Routing\Exception\NoConfigurationException;
21 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
22 use Symfony\Component\Routing\Matcher\UrlMatcher;
23 use Symfony\Component\Routing\RequestContext;
37 
53 {
57  protected ‪$finder;
58 
62  protected ‪$pseudoSiteFinder;
63 
69  public function ‪__construct(‪SiteFinder ‪$finder = null)
70  {
71  $this->finder = ‪$finder ?? GeneralUtility::makeInstance(SiteFinder::class);
72  $this->pseudoSiteFinder = GeneralUtility::makeInstance(PseudoSiteFinder::class);
73  }
74 
82  public function ‪refresh()
83  {
86  GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_rootline')->flush();
87  $this->pseudoSiteFinder = GeneralUtility::makeInstance(PseudoSiteFinder::class);
88  $this->pseudoSiteFinder->refresh();
89  }
90 
104  public function ‪matchRequest(ServerRequestInterface $request): ‪RouteResultInterface
105  {
106  $site = null;
107  $language = null;
108  $defaultLanguage = null;
109 
110  $pageId = $request->getQueryParams()['id'] ?? $request->getParsedBody()['id'] ?? 0;
111 
112  if (!empty($pageId) && !‪MathUtility::canBeInterpretedAsInteger($pageId)) {
113  $pageId = (int)GeneralUtility::makeInstance(PageRepository::class)->getPageIdFromAlias($pageId);
114  }
115  // First, check if we have a _GET/_POST parameter for "id", then a site information can be resolved based.
116  if ($pageId > 0) {
117  // Loop over the whole rootline without permissions to get the actual site information
118  try {
119  $site = $this->finder->getSiteByPageId((int)$pageId);
120  // If a "L" parameter is given, we take that one into account.
121  $languageId = $request->getQueryParams()['L'] ?? $request->getParsedBody()['L'] ?? null;
122  if ($languageId !== null) {
123  $language = $site->getLanguageById((int)$languageId);
124  } else {
125  // Use this later below
126  $defaultLanguage = $site->getDefaultLanguage();
127  }
128  } catch (‪SiteNotFoundException $e) {
129  // No site found by the given page
130  } catch (\InvalidArgumentException $e) {
131  // The language fetched by getLanguageById() was not available, now the PSR-15 middleware
132  // redirects to the default page.
133  }
134  }
135 
136  // No language found at this point means that the URL was not used with a valid "?id=1&L=2" parameter
137  // which resulted in a site / language combination that was found. Now, the matching is done
138  // on the incoming URL.
139  if (!($language instanceof ‪SiteLanguage)) {
140  $collection = $this->‪getRouteCollectionForAllSites();
141  $context = new RequestContext(
142  '',
143  $request->getMethod(),
144  ‪HttpUtility::idn_to_ascii($request->getUri()->getHost()),
145  $request->getUri()->getScheme(),
146  // Ports are only necessary for URL generation in Symfony which is not used by TYPO3
147  80,
148  443,
149  $request->getUri()->getPath()
150  );
151  $matcher = new UrlMatcher($collection, $context);
152  try {
153  $result = $matcher->match($request->getUri()->getPath());
154  return new ‪SiteRouteResult(
155  $request->getUri(),
156  $result['site'],
157  // if no language is found, this usually results due to "/" called instead of "/fr/"
158  // but it could also be the reason that "/index.php?id=23" was called, so the default
159  // language is used as a fallback here then.
160  $result['language'] ?? $defaultLanguage,
161  $result['tail']
162  );
163  } catch (NoConfigurationException | ResourceNotFoundException $e) {
164  // No site+language combination found so far
165  }
166  // At this point we discard a possible found site via ?id=123
167  // Because ?id=123 _can_ only work if the actual domain/site base works
168  // so www.domain-without-site-configuration/index.php?id=123 (where 123 is a page referring
169  // to a page within a site configuration will never be resolved here) properly
170  $site = null;
171  }
172 
173  // Check against any sys_domain records
174  $collection = $this->‪getRouteCollectionForVisibleSysDomains();
175  $context = new RequestContext('/', $request->getMethod(), $request->getUri()->getHost());
176  $matcher = new UrlMatcher($collection, $context);
177  if ((bool)‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['recursiveDomainSearch']) {
178  $host = explode('.', $request->getUri()->getHost());
179  while (count($host)) {
180  $context->setHost(implode('.', $host));
181  try {
182  $result = $matcher->match($request->getUri()->getPath());
183  return new ‪SiteRouteResult(
184  $request->getUri(),
185  $result['site'],
186  $result['language'],
187  $result['tail']
188  );
189  } catch (NoConfigurationException | ResourceNotFoundException $e) {
190  array_shift($host);
191  }
192  }
193  } else {
194  try {
195  $result = $matcher->match($request->getUri()->getPath());
196  return new ‪SiteRouteResult($request->getUri(), $result['site'], $result['language'], $result['tail']);
197  } catch (NoConfigurationException | ResourceNotFoundException $e) {
198  // No domain record found
199  }
200  }
201  // No domain record found, try resolving "pseudo-site" again
202  if ($site == null) {
203  try {
204  // use the matching "pseudo-site" for $pageId
205  $site = $this->pseudoSiteFinder->getSiteByPageId((int)$pageId);
206  } catch (‪SiteNotFoundException $exception) {
207  // use the first "pseudo-site" found
208  $allPseudoSites = $this->pseudoSiteFinder->findAll();
209  $site = reset($allPseudoSites);
210  }
211  }
212  return new ‪SiteRouteResult($request->getUri(), $site, $language);
213  }
214 
223  public function ‪matchByPageId(int $pageId, array $rootLine = null): ‪SiteInterface
224  {
225  try {
226  return $this->finder->getSiteByPageId($pageId, $rootLine);
227  } catch (‪SiteNotFoundException $e) {
228  // Check for a pseudo / null site
229  return $this->pseudoSiteFinder->getSiteByPageId($pageId, $rootLine);
230  }
231  }
232 
239  {
240  $groupedRoutes = [];
241  foreach ($this->finder->getAllSites() as $site) {
242  // Add the site as entrypoint
243  $uri = $site->getBase();
244  $route = new ‪Route(
245  ($uri->getPath() ?: '/') . '{tail}',
246  ['site' => $site, 'language' => null, 'tail' => ''],
247  array_filter(['tail' => '.*', 'port' => (string)$uri->getPort()]),
248  ['utf8' => true],
249  $uri->getHost() ?: '',
250  $uri->getScheme()
251  );
252  $identifier = 'site_' . $site->getIdentifier();
253  $groupedRoutes[($uri->getScheme() ?: '-') . ($uri->getHost() ?: '-')][$uri->getPath() ?: '/'][$identifier] = $route;
254  // Add all languages
255  foreach ($site->getAllLanguages() as $siteLanguage) {
256  $uri = $siteLanguage->getBase();
257  $route = new ‪Route(
258  ($uri->getPath() ?: '/') . '{tail}',
259  ['site' => $site, 'language' => $siteLanguage, 'tail' => ''],
260  array_filter(['tail' => '.*', 'port' => (string)$uri->getPort()]),
261  ['utf8' => true],
262  ‪HttpUtility::idn_to_ascii($uri->getHost()) ?: '',
263  $uri->getScheme()
264  );
265  $identifier = 'site_' . $site->getIdentifier() . '_' . $siteLanguage->getLanguageId();
266  $groupedRoutes[($uri->getScheme() ?: '-') . ($uri->getHost() ?: '-')][$uri->getPath() ?: '/'][$identifier] = $route;
267  }
268  }
269  return $this->‪createRouteCollectionFromGroupedRoutes($groupedRoutes);
270  }
271 
280  {
281  $sites = $this->pseudoSiteFinder->findAll();
282  $groupedRoutes = [];
283  foreach ($sites as $site) {
284  if (!$site instanceof ‪PseudoSite) {
285  continue;
286  }
287  foreach ($site->getEntryPoints() as $uri) {
288  // Site has no sys_domain record, it is not valid for a routing entrypoint, but only available
289  // via "id" GET parameter which is handled separately
290  if (!$uri->getHost()) {
291  continue;
292  }
293  $route = new ‪Route(
294  ($uri->getPath() ?: '/') . '{tail}',
295  ['site' => $site, 'language' => null, 'tail' => ''],
296  array_filter(['tail' => '.*', 'port' => (string)$uri->getPort()]),
297  ['utf8' => true],
298  $uri->getHost(),
299  $uri->getScheme()
300  );
301  $identifier = 'site_' . $site->getIdentifier() . '_' . (string)$uri;
302  $groupedRoutes[($uri->getScheme() ?: '-') . ($uri->getHost() ?: '-')][$uri->getPath() ?: '/'][$identifier] = $route;
303  }
304  }
305  return $this->‪createRouteCollectionFromGroupedRoutes($groupedRoutes);
306  }
307 
315  protected function ‪createRouteCollectionFromGroupedRoutes(array $groupedRoutes): ‪RouteCollection
316  {
317  $collection = new ‪RouteCollection();
318  // Ensure more generic routes containing '-' in host identifier, processed at last
319  krsort($groupedRoutes);
320  foreach ($groupedRoutes as $groupedRoutesPerHost) {
321  krsort($groupedRoutesPerHost);
322  foreach ($groupedRoutesPerHost as $groupedRoutesPerPath) {
323  krsort($groupedRoutesPerPath);
324  foreach ($groupedRoutesPerPath as $identifier => $route) {
325  $collection->add($identifier, $route);
326  }
327  }
328  }
329  return $collection;
330  }
331 }
‪TYPO3\CMS\Core\Site\Entity\SiteInterface
Definition: SiteInterface.php:25
‪TYPO3\CMS\Core\Utility\HttpUtility\idn_to_ascii
‪static string bool idn_to_ascii(string $domain)
Definition: HttpUtility.php:193
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:73
‪TYPO3\CMS\Core\Routing\RouteResultInterface
Definition: RouteResultInterface.php:23
‪TYPO3\CMS\Core\Routing\RouteCollection
Definition: RouteCollection.php:27
‪TYPO3\CMS\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:36
‪TYPO3\CMS\Core\Utility\RootlineUtility\purgeCaches
‪static purgeCaches()
Definition: RootlineUtility.php:164
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Routing
‪TYPO3\CMS\Core\Routing\SiteMatcher\__construct
‪__construct(SiteFinder $finder=null)
Definition: SiteMatcher.php:67
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:25
‪TYPO3\CMS\Core\Site\PseudoSiteFinder
Definition: PseudoSiteFinder.php:39
‪TYPO3\CMS\Core\Routing\SiteRouteResult
Definition: SiteRouteResult.php:29
‪TYPO3\CMS\Core\Routing\SiteMatcher\getRouteCollectionForAllSites
‪RouteCollection getRouteCollectionForAllSites()
Definition: SiteMatcher.php:236
‪TYPO3\CMS\Core\Routing\SiteMatcher\$pseudoSiteFinder
‪PseudoSiteFinder $pseudoSiteFinder
Definition: SiteMatcher.php:60
‪TYPO3\CMS\Frontend\Page\PageRepository
Definition: PageRepository.php:53
‪TYPO3\CMS\Core\Routing\SiteMatcher\getRouteCollectionForVisibleSysDomains
‪RouteCollection getRouteCollectionForVisibleSysDomains()
Definition: SiteMatcher.php:277
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:34
‪TYPO3\CMS\Core\Routing\SiteMatcher\$finder
‪SiteFinder $finder
Definition: SiteMatcher.php:56
‪TYPO3\CMS\Core\Routing\SiteMatcher\refresh
‪refresh()
Definition: SiteMatcher.php:80
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Routing\SiteMatcher\createRouteCollectionFromGroupedRoutes
‪RouteCollection createRouteCollectionFromGroupedRoutes(array $groupedRoutes)
Definition: SiteMatcher.php:313
‪TYPO3\CMS\Core\Routing\Route
Definition: Route.php:31
‪TYPO3\CMS\Core\Site\Entity\PseudoSite
Definition: PseudoSite.php:28
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:21
‪TYPO3\CMS\Core\Routing\SiteMatcher\matchRequest
‪RouteResultInterface matchRequest(ServerRequestInterface $request)
Definition: SiteMatcher.php:102
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:21
‪TYPO3\CMS\Core\Routing\SiteMatcher\matchByPageId
‪SiteInterface matchByPageId(int $pageId, array $rootLine=null)
Definition: SiteMatcher.php:221
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Routing\SiteMatcher
Definition: SiteMatcher.php:53