[TASK] Add API to hide PseudoSite functionality 92/57992/6
authorBenni Mack <benni@typo3.org>
Wed, 22 Aug 2018 17:18:57 +0000 (19:18 +0200)
committerAndreas Fernandez <a.fernandez@scripting-base.de>
Wed, 22 Aug 2018 19:59:46 +0000 (21:59 +0200)
In order to avoid usages of using PseudoSite objects directly,
the following changes have been made:

The main entrypoint for finding sites is now encapsulated the
SiteMatcher class.

* SiteMatcher->matchByPageId($pageId)
which consistently checks for Sites and Pseudo Sites. This is now
a singleton as it factors PseudoSiteFinder directly.

* NullSite for PID=0 (which is a slim version of a PseudoSite
and will stay around for longer than the PseudoSite).

This marks the last part of the Site API functionality, as
TYPO3 is now ready to work with the Site API in all places
throughout Frontend and Backend.

Resolves: #85930
Releases: master
Change-Id: I5e75da22337e126a39b459388e21d7f9fe05e556
Reviewed-on: https://review.typo3.org/57992
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
12 files changed:
typo3/sysext/backend/Classes/Controller/EditDocumentController.php
typo3/sysext/backend/Classes/Form/FormDataProvider/SiteResolving.php
typo3/sysext/backend/Classes/Middleware/SiteResolver.php
typo3/sysext/backend/Classes/View/PageLayoutView.php
typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/SiteResolvingTest.php
typo3/sysext/core/Classes/Routing/SiteMatcher.php
typo3/sysext/core/Classes/Site/Entity/NullSite.php [new file with mode: 0644]
typo3/sysext/core/Classes/Site/Entity/PseudoSite.php
typo3/sysext/core/Classes/Site/Entity/SiteInterface.php
typo3/sysext/core/Classes/Site/PseudoSiteFinder.php
typo3/sysext/core/Classes/Site/SiteFinder.php
typo3/sysext/frontend/Tests/Unit/Middleware/SiteResolverTest.php

index d05354b..bbbbe09 100644 (file)
@@ -36,16 +36,14 @@ use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\ReferenceIndex;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
-use TYPO3\CMS\Core\Exception\SiteNotFoundException;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\RedirectResponse;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
-use TYPO3\CMS\Core\Site\PseudoSiteFinder;
-use TYPO3\CMS\Core\Site\SiteFinder;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\HttpUtility;
@@ -2428,12 +2426,7 @@ class EditDocumentController
      */
     protected function getLanguages(int $id, string $table): array
     {
-        try {
-            $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($id);
-        } catch (SiteNotFoundException $e) {
-            // Check for a pseudo site
-            $site = GeneralUtility::makeInstance(PseudoSiteFinder::class)->getSiteByPageId($id);
-        }
+        $site = GeneralUtility::makeInstance(SiteMatcher::class)->matchByPageId($id);
 
         // Fetch the current translations of this page, to only show the ones where there is a page translation
         $allLanguages = $site->getAvailableLanguages($this->getBackendUser(), false, $id);
index 8a912ef..b65186b 100644 (file)
@@ -16,9 +16,7 @@ namespace TYPO3\CMS\Backend\Form\FormDataProvider;
  */
 
 use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
-use TYPO3\CMS\Core\Exception\SiteNotFoundException;
-use TYPO3\CMS\Core\Site\PseudoSiteFinder;
-use TYPO3\CMS\Core\Site\SiteFinder;
+use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -39,12 +37,7 @@ class SiteResolving implements FormDataProviderInterface
     public function addData(array $result): array
     {
         $pageIdDefaultLanguage = $result['defaultLanguagePageRow']['uid'] ?? $result['effectivePid'];
-        try {
-            $result['site'] = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageIdDefaultLanguage);
-        } catch (SiteNotFoundException $e) {
-            // Check for a pseudo site
-            $result['site'] = GeneralUtility::makeInstance(PseudoSiteFinder::class)->getSiteByPageId($pageIdDefaultLanguage);
-        }
+        $result['site'] = GeneralUtility::makeInstance(SiteMatcher::class)->matchByPageId($pageIdDefaultLanguage);
         return $result;
     }
 }
index e1a4178..ada9d7c 100644 (file)
@@ -20,10 +20,7 @@ use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Core\Exception\SiteNotFoundException;
-use TYPO3\CMS\Core\Site\Entity\SiteInterface;
-use TYPO3\CMS\Core\Site\PseudoSiteFinder;
-use TYPO3\CMS\Core\Site\SiteFinder;
+use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -49,25 +46,14 @@ class SiteResolver implements MiddlewareInterface
         $site = null;
         $pageId = (int)($request->getQueryParams()['id'] ?? $request->getParsedBody()['id'] ?? 0);
 
-        // Check if we have a _GET/_POST parameter for "id", then a site information can be resolved based.
+        $rootLine = null;
         if ($pageId > 0) {
-            try {
-                $finder = GeneralUtility::makeInstance(SiteFinder::class);
-                $site = $finder->getSiteByPageId($pageId);
-            } catch (SiteNotFoundException $e) {
-                // Check for pseudo sites, based on given ID
-                $finder = GeneralUtility::makeInstance(PseudoSiteFinder::class);
-                $rootLine = BackendUtility::BEgetRootLine($pageId);
-                $site = $finder->getSiteByPageId($pageId, $rootLine);
-            }
-        } else {
-            $finder = GeneralUtility::makeInstance(PseudoSiteFinder::class);
-            $site = $finder->getSiteByPageId(0);
-        }
-        if ($site instanceof SiteInterface) {
-            $request = $request->withAttribute('site', $site);
-            $GLOBALS['TYPO3_REQUEST'] = $request;
+            // Check if we have a _GET/_POST parameter for "id", then a site information can be resolved based.
+            $rootLine = BackendUtility::BEgetRootLine($pageId);
         }
+        $site = GeneralUtility::makeInstance(SiteMatcher::class)->matchByPageId($pageId, $rootLine);
+        $request = $request->withAttribute('site', $site);
+        $GLOBALS['TYPO3_REQUEST'] = $request;
         return $handler->handle($request);
     }
 }
index 018d6a9..aae4213 100644 (file)
@@ -39,12 +39,10 @@ use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Service\DependencyOrderingService;
 use TYPO3\CMS\Core\Service\FlexFormService;
-use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
-use TYPO3\CMS\Core\Site\PseudoSiteFinder;
-use TYPO3\CMS\Core\Site\SiteFinder;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -4406,12 +4404,7 @@ class PageLayoutView implements LoggerAwareInterface
      */
     protected function resolveSiteLanguages(int $pageId)
     {
-        try {
-            $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageId);
-        } catch (SiteNotFoundException $e) {
-            // Check for a pseudo site
-            $site = GeneralUtility::makeInstance(PseudoSiteFinder::class)->getSiteByPageId($pageId);
-        }
+        $site = GeneralUtility::makeInstance(SiteMatcher::class)->matchByPageId($pageId);
         $this->siteLanguages = $site->getAvailableLanguages($this->getBackendUser(), false, $pageId);
     }
 
index 7b4d51f..df12fbe 100644 (file)
@@ -16,8 +16,8 @@ namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
  */
 
 use TYPO3\CMS\Backend\Form\FormDataProvider\SiteResolving;
+use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Site\Entity\Site;
-use TYPO3\CMS\Core\Site\SiteFinder;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
@@ -26,16 +26,18 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
  */
 class SiteResolvingTest extends UnitTestCase
 {
+    protected $resetSingletonInstances = true;
+
     /**
      * @test
      */
     public function addDataAddsSiteObjectOfDefaultLanguageRow()
     {
-        $siteFinderProphecy = $this->prophesize(SiteFinder::class);
-        GeneralUtility::addInstance(SiteFinder::class, $siteFinderProphecy->reveal());
+        $siteMatcherProphecy = $this->prophesize(SiteMatcher::class);
+        GeneralUtility::setSingletonInstance(SiteMatcher::class, $siteMatcherProphecy->reveal());
         $siteProphecy = $this->prophesize(Site::class);
         $siteProphecyRevelation = $siteProphecy->reveal();
-        $siteFinderProphecy->getSiteByPageId(23)->willReturn($siteProphecyRevelation);
+        $siteMatcherProphecy->matchByPageId(23)->willReturn($siteProphecyRevelation);
         $input = [
             'defaultLanguagePageRow' => [
                 'uid' => 23,
@@ -53,11 +55,11 @@ class SiteResolvingTest extends UnitTestCase
      */
     public function addDataAddsSiteObjectOfEffectivePid()
     {
-        $siteFinderProphecy = $this->prophesize(SiteFinder::class);
-        GeneralUtility::addInstance(SiteFinder::class, $siteFinderProphecy->reveal());
+        $siteMatcherProphecy = $this->prophesize(SiteMatcher::class);
+        GeneralUtility::setSingletonInstance(SiteMatcher::class, $siteMatcherProphecy->reveal());
         $siteProphecy = $this->prophesize(Site::class);
         $siteProphecyRevelation = $siteProphecy->reveal();
-        $siteFinderProphecy->getSiteByPageId(42)->willReturn($siteProphecyRevelation);
+        $siteMatcherProphecy->matchByPageId(42)->willReturn($siteProphecyRevelation);
         $input = [
             'effectivePid' => 42,
             'site' => $siteProphecyRevelation,
index 5535ab1..b255b56 100644 (file)
@@ -24,6 +24,9 @@ use Symfony\Component\Routing\RequestContext;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
+use TYPO3\CMS\Core\SingletonInterface;
+use TYPO3\CMS\Core\Site\Entity\PseudoSite;
+use TYPO3\CMS\Core\Site\Entity\SiteInterface;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
 use TYPO3\CMS\Core\Site\PseudoSiteFinder;
 use TYPO3\CMS\Core\Site\SiteFinder;
@@ -36,8 +39,11 @@ use TYPO3\CMS\Frontend\Page\PageRepository;
  *
  * The main usage is the ->matchRequest() functionality, which receives a request object and boots up
  * Symfony Routing to find the proper route with its defaults / attributes.
+ *
+ * On top, this is also commonly used throughout TYPO3 to fetch a site by a given pageId.
+ * ->matchPageId()
  */
-class SiteMatcher
+class SiteMatcher implements SingletonInterface
 {
     /**
      * @var SiteFinder
@@ -45,12 +51,19 @@ class SiteMatcher
     protected $finder;
 
     /**
-     * Injects necessary objects
+     * @var PseudoSiteFinder
+     */
+    protected $pseudoSiteFinder;
+
+    /**
+     * Injects necessary objects. PseudoSiteFinder is not injectable as this will be become obsolete in the future.
+     *
      * @param SiteFinder|null $finder
      */
     public function __construct(SiteFinder $finder = null)
     {
         $this->finder = $finder ?? GeneralUtility::makeInstance(SiteFinder::class);
+        $this->pseudoSiteFinder = GeneralUtility::makeInstance(PseudoSiteFinder::class);
     }
 
     /**
@@ -142,6 +155,23 @@ class SiteMatcher
     }
 
     /**
+     * If a given page ID is handed in, a Site/PseudoSite/NullSite is returned.
+     *
+     * @param int $pageId the page ID (must be a page in the default language)
+     * @param array|null $rootLine an alternative root line, if already at and.
+     * @return SiteInterface
+     */
+    public function matchByPageId(int $pageId, array $rootLine = null): SiteInterface
+    {
+        try {
+            return $this->finder->getSiteByPageId($pageId, $rootLine);
+        } catch (SiteNotFoundException $e) {
+            // Check for a pseudo / null site
+            return $this->pseudoSiteFinder->getSiteByPageId($pageId, $rootLine);
+        }
+    }
+
+    /**
      * Returns a Symfony RouteCollection containing all routes to all sites.
      *
      * {next} is not evaluated yet, but set as suffix and will change in the future.
@@ -178,9 +208,12 @@ class SiteMatcher
      */
     protected function getRouteCollectionForVisibleSysDomains(): RouteCollection
     {
-        $sites = GeneralUtility::makeInstance(PseudoSiteFinder::class)->findAll();
+        $sites = $this->pseudoSiteFinder->findAll();
         $groupedRoutes = [];
         foreach ($sites as $site) {
+            if (!$site instanceof PseudoSite) {
+                continue;
+            }
             foreach ($site->getEntryPoints() as $domainName) {
                 // Site has no sys_domain record, it is not valid for a routing entrypoint, but only available
                 // via "id" GET parameter which is handled before
diff --git a/typo3/sysext/core/Classes/Site/Entity/NullSite.php b/typo3/sysext/core/Classes/Site/Entity/NullSite.php
new file mode 100644 (file)
index 0000000..1950153
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Core\Site\Entity;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
+use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Entity representing a site for everything on "pid=0". Mostly used in TYPO3 Backend, not really in use elsewhere.
+ */
+class NullSite implements SiteInterface
+{
+    /**
+     * @var int
+     */
+    protected $rootPageId = 0;
+
+    /**
+     * @var SiteLanguage[]
+     */
+    protected $languages;
+
+    /**
+     * Sets up a null site object
+     *
+     * @param array $languages (sys_language objects)
+     */
+    public function __construct(array $languages = null)
+    {
+        foreach ($languages ?? [] as $languageConfiguration) {
+            $languageUid = (int)$languageConfiguration['languageId'];
+            // Language configuration does not have a base defined
+            // So the main site base is used (usually done for default languages)
+            $base = '/';
+            $this->languages[$languageUid] = new SiteLanguage(
+                $languageUid,
+                $languageConfiguration['locale'] ?? '',
+                $base,
+                $languageConfiguration
+            );
+        }
+    }
+
+    /**
+     * Returns always #NULL
+     *
+     * @return string
+     */
+    public function getIdentifier(): string
+    {
+        return '#NULL';
+    }
+
+    /**
+     * Always "/"
+     */
+    public function getBase(): string
+    {
+        return '/';
+    }
+
+    /**
+     * Always zero
+     *
+     * @return int
+     */
+    public function getRootPageId(): int
+    {
+        return 0;
+    }
+
+    /**
+     * Returns all available languages of this installation
+     *
+     * @return SiteLanguage[]
+     */
+    public function getLanguages(): array
+    {
+        return $this->languages;
+    }
+
+    /**
+     * Returns a language of this site, given by the sys_language_uid
+     *
+     * @param int $languageId
+     * @return SiteLanguage
+     * @throws \InvalidArgumentException
+     */
+    public function getLanguageById(int $languageId): SiteLanguage
+    {
+        if (isset($this->languages[$languageId])) {
+            return $this->languages[$languageId];
+        }
+        throw new \InvalidArgumentException(
+            'Language ' . $languageId . ' does not exist on site ' . $this->getIdentifier() . '.',
+            1522965188
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function getDefaultLanguage(): SiteLanguage
+    {
+        return reset($this->languages);
+    }
+
+    /**
+     * This takes pageTSconfig into account (unlike Site interface) to find
+     * mod.SHARED.disableLanguages and mod.SHARED.defaultLanguageLabel
+     *
+     * @inheritdoc
+     */
+    public function getAvailableLanguages(BackendUserAuthentication $user, bool $includeAllLanguagesFlag = false, int $pageId = null): array
+    {
+        $availableLanguages = [];
+
+        // Check if we need to add language "-1"
+        if ($includeAllLanguagesFlag && $user->checkLanguageAccess(-1)) {
+            $availableLanguages[-1] = new SiteLanguage(-1, '', $this->getBase(), [
+                'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:multipleLanguages'),
+                'flag' => 'flag-multiple'
+            ]);
+        }
+        $pageTs = BackendUtility::getPagesTSconfig($pageId);
+        $pageTs = $pageTs['mod.']['SHARED.'] ?? [];
+
+        $disabledLanguages = GeneralUtility::intExplode(',', $pageTs['disableLanguages'] ?? '', true);
+        // Do not add the ones that are not allowed by the user
+        foreach ($this->languages as $language) {
+            if ($user->checkLanguageAccess($language->getLanguageId()) && !in_array($language->getLanguageId(), $disabledLanguages, true)) {
+                if ($language->getLanguageId() === 0) {
+                    // 0: "Default" language
+                    $defaultLanguageLabel = 'LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:defaultLanguage';
+                    $defaultLanguageLabel = $this->getLanguageService()->sL($defaultLanguageLabel);
+                    if (isset($pageTs['defaultLanguageLabel'])) {
+                        $defaultLanguageLabel = $pageTs['defaultLanguageLabel'] . ' (' . $defaultLanguageLabel . ')';
+                    }
+                    $defaultLanguageFlag = '';
+                    if (isset($pageTs['defaultLanguageFlag'])) {
+                        $defaultLanguageFlag = 'flags-' . $pageTs['defaultLanguageFlag'];
+                    }
+                    $language = new SiteLanguage(0, '', $language->getBase(), [
+                        'title' => $defaultLanguageLabel,
+                        'flag' => $defaultLanguageFlag,
+                    ]);
+                }
+                $availableLanguages[$language->getLanguageId()] = $language;
+            }
+        }
+
+        return $availableLanguages;
+    }
+
+    /**
+     * Returns a ready-to-use error handler, to be used within the ErrorController
+     *
+     * @param int $statusCode
+     * @return PageErrorHandlerInterface
+     * @throws \RuntimeException
+     */
+    public function getErrorHandler(int $statusCode): PageErrorHandlerInterface
+    {
+        throw new \RuntimeException('No error handler given for the status code "' . $statusCode . '".', 1522495102);
+    }
+
+    /**
+     * Shorthand functionality for fetching the language service
+     * @return LanguageService
+     */
+    protected function getLanguageService(): LanguageService
+    {
+        return $GLOBALS['LANG'];
+    }
+}
index b862e92..13b3e79 100644 (file)
@@ -16,16 +16,12 @@ namespace TYPO3\CMS\Core\Site\Entity;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
-use TYPO3\CMS\Core\Localization\LanguageService;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
 /**
- * Entity representing a site with legacy configuration (sys_domain) and all available languages in the system (sys_language)
+ * Entity representing a site with legacy configuration (sys_domain) and all available
+ * languages in the system (sys_language)
+ * @internal this class will likely be removed in TYPO3 v10.0. Please use SiteMatcher and SiteInterface to work with Sites in your own code.
  */
-class PseudoSite implements SiteInterface
+class PseudoSite extends NullSite implements SiteInterface
 {
     /**
      * @var string[]
@@ -33,16 +29,6 @@ class PseudoSite implements SiteInterface
     protected $entryPoints;
 
     /**
-     * @var int
-     */
-    protected $rootPageId;
-
-    /**
-     * @var SiteLanguage[]
-     */
-    protected $languages;
-
-    /**
      * attached sys_domain records
      * @var array
      */
@@ -89,7 +75,7 @@ class PseudoSite implements SiteInterface
      */
     public function getIdentifier(): string
     {
-        return 'PSEUDO_' . $this->rootPageId;
+        return '#PSEUDO_' . $this->rootPageId;
     }
 
     /**
@@ -121,101 +107,6 @@ class PseudoSite implements SiteInterface
     }
 
     /**
-     * Returns all available languages of this site
-     *
-     * @return SiteLanguage[]
-     */
-    public function getLanguages(): array
-    {
-        return $this->languages;
-    }
-
-    /**
-     * Returns a language of this site, given by the sys_language_uid
-     *
-     * @param int $languageId
-     * @return SiteLanguage
-     * @throws \InvalidArgumentException
-     */
-    public function getLanguageById(int $languageId): SiteLanguage
-    {
-        if (isset($this->languages[$languageId])) {
-            return $this->languages[$languageId];
-        }
-        throw new \InvalidArgumentException(
-            'Language ' . $languageId . ' does not exist on site ' . $this->getIdentifier() . '.',
-            1522965188
-        );
-    }
-
-    /**
-     * @inheritdoc
-     */
-    public function getDefaultLanguage(): SiteLanguage
-    {
-        return reset($this->languages);
-    }
-
-    /**
-     * This takes pageTSconfig into account (unlike Site interface) to find
-     * mod.SHARED.disableLanguages and mod.SHARED.defaultLanguageLabel
-     *
-     * @inheritdoc
-     */
-    public function getAvailableLanguages(BackendUserAuthentication $user, bool $includeAllLanguagesFlag = false, int $pageId = null): array
-    {
-        $availableLanguages = [];
-
-        // Check if we need to add language "-1"
-        if ($includeAllLanguagesFlag && $user->checkLanguageAccess(-1)) {
-            $availableLanguages[-1] = new SiteLanguage(-1, '', $this->getBase(), [
-                'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:multipleLanguages'),
-                'flag' => 'flag-multiple'
-            ]);
-        }
-        $pageTs = BackendUtility::getPagesTSconfig($pageId);
-        $pageTs = $pageTs['mod.']['SHARED.'] ?? [];
-
-        $disabledLanguages = GeneralUtility::intExplode(',', $pageTs['disableLanguages'] ?? '', true);
-        // Do not add the ones that are not allowed by the user
-        foreach ($this->languages as $language) {
-            if ($user->checkLanguageAccess($language->getLanguageId()) && !in_array($language->getLanguageId(), $disabledLanguages, true)) {
-                if ($language->getLanguageId() === 0) {
-                    // 0: "Default" language
-                    $defaultLanguageLabel = 'LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:defaultLanguage';
-                    $defaultLanguageLabel = $this->getLanguageService()->sL($defaultLanguageLabel);
-                    if (isset($pageTs['defaultLanguageLabel'])) {
-                        $defaultLanguageLabel = $pageTs['defaultLanguageLabel'] . ' (' . $defaultLanguageLabel . ')';
-                    }
-                    $defaultLanguageFlag = '';
-                    if (isset($pageTs['defaultLanguageFlag'])) {
-                        $defaultLanguageFlag = 'flags-' . $pageTs['defaultLanguageFlag'];
-                    }
-                    $language = new SiteLanguage(0, '', $language->getBase(), [
-                        'title' => $defaultLanguageLabel,
-                        'flag' => $defaultLanguageFlag,
-                    ]);
-                }
-                $availableLanguages[$language->getLanguageId()] = $language;
-            }
-        }
-
-        return $availableLanguages;
-    }
-
-    /**
-     * Returns a ready-to-use error handler, to be used within the ErrorController
-     *
-     * @param int $statusCode
-     * @return PageErrorHandlerInterface
-     * @throws \RuntimeException
-     */
-    public function getErrorHandler(int $statusCode): PageErrorHandlerInterface
-    {
-        throw new \RuntimeException('No error handler given for the status code "' . $statusCode . '".', 1522495102);
-    }
-
-    /**
      * If a site base contains "/" or "www.domain.com", it is ensured that
      * parse_url() can handle this kind of configuration properly.
      *
@@ -239,13 +130,4 @@ class PseudoSite implements SiteInterface
         }
         return $base;
     }
-
-    /**
-     * Shorthand functionality for fetching the language service
-     * @return LanguageService
-     */
-    protected function getLanguageService(): LanguageService
-    {
-        return $GLOBALS['LANG'];
-    }
 }
index 4a9a5cd..3dece0e 100644 (file)
@@ -30,6 +30,13 @@ interface SiteInterface
     public function getRootPageId(): int;
 
     /**
+     * Returns an identifier for the site / configuration
+     *
+     * @return string
+     */
+    public function getIdentifier(): string;
+
+    /**
      * Returns all available languages of this site visible in the frontend
      *
      * @return SiteLanguage[]
index 471f2e9..8fe6efa 100644 (file)
@@ -23,7 +23,7 @@ use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
 use TYPO3\CMS\Core\Exception\Page\PageNotFoundException;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
-use TYPO3\CMS\Core\SingletonInterface;
+use TYPO3\CMS\Core\Site\Entity\NullSite;
 use TYPO3\CMS\Core\Site\Entity\PseudoSite;
 use TYPO3\CMS\Core\Site\Entity\SiteInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -32,9 +32,9 @@ use TYPO3\CMS\Frontend\Compatibility\LegacyDomainResolver;
 
 /**
  * Methods related to "pseudo-sites" = sites that do not have a configuration yet.
- * @internal
+ * @internal this class will likely be removed in TYPO3 v10.0. Please use SiteMatcher and not the PseudoSiteFinder directly to make use of caching etc.
  */
-class PseudoSiteFinder implements SingletonInterface
+class PseudoSiteFinder
 {
     /**
      * @var string
@@ -54,7 +54,6 @@ class PseudoSiteFinder implements SingletonInterface
     public function __construct()
     {
         $this->cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_core');
-        $this->populate();
     }
 
     /**
@@ -96,7 +95,7 @@ class PseudoSiteFinder implements SingletonInterface
         }
 
         // Now lets an empty Pseudo-Site for visiting things on pid=0
-        $this->pseudoSites[0] = new PseudoSite(0, ['languages' => $allLanguages]);
+        $this->pseudoSites[0] = new NullSite($allLanguages);
     }
 
     /**
index 47a9060..6a2986d 100644 (file)
@@ -68,9 +68,10 @@ class SiteFinder
     /**
      * Find a site by given root page id
      *
-     * @param int $rootPageId
+     * @param int $rootPageId the page ID (default language)
      * @return SiteInterface
      * @throws SiteNotFoundException
+     * @internal only for usage in some places for managing Site Configuration, might be removed without further notice
      */
     public function getSiteByRootPageId(int $rootPageId): SiteInterface
     {
@@ -116,12 +117,8 @@ class SiteFinder
             }
         }
         foreach ($rootLine as $pageInRootLine) {
-            if ($pageInRootLine['uid'] > 0) {
-                try {
-                    return $this->getSiteByRootPageId((int)$pageInRootLine['uid']);
-                } catch (SiteNotFoundException $e) {
-                    // continue looping
-                }
+            if (isset($this->mappingRootPageIdToIdentifier[(int)$pageInRootLine['uid']])) {
+                return $this->sites[$this->mappingRootPageIdToIdentifier[(int)$pageInRootLine['uid']]];
             }
         }
         throw new SiteNotFoundException('No site found in root line of page  ' . $pageId, 1521716622);
index c2834fd..4876916 100644 (file)
@@ -19,6 +19,7 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\Middleware;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\RequestHandlerInterface;
+use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Http\NullResponse;
 use TYPO3\CMS\Core\Http\ServerRequest;
@@ -26,6 +27,7 @@ use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
 use TYPO3\CMS\Core\Site\SiteFinder;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Middleware\SiteResolver;
 use TYPO3\CMS\Frontend\Tests\Functional\SiteHandling\Fixtures\PhpError;
 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
@@ -75,6 +77,9 @@ class SiteResolverTest extends UnitTestCase
                 return new NullResponse();
             }
         };
+
+        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
+        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
     }
 
     /**