TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:09 +02:00
commit af8cc155b5
6818 changed files with 642608 additions and 0 deletions
+183
View File
@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Site\Entity;
use Psr\Http\Message\UriInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Http\Uri;
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
{
protected int $rootPageId = 0;
/**
* @var SiteLanguage[]
*/
protected array $languages;
/**
* Sets up a null site object
*
* @param array|null $languages site languages
* @param Uri|null $baseEntryPoint
*/
public function __construct(?array $languages = null, ?Uri $baseEntryPoint = null)
{
if (empty($languages)) {
// Create the default language if no language configuration is given
$this->languages[0] = new SiteLanguage(
0,
'',
new Uri('/'),
['enabled' => true]
);
} else {
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)
$this->languages[$languageUid] = new SiteLanguage(
$languageUid,
$languageConfiguration['locale'] ?? '',
$baseEntryPoint ?: new Uri('/'),
$languageConfiguration
);
}
}
}
/**
* Returns always #NULL
*/
public function getIdentifier(): string
{
return '#NULL';
}
/**
* Always "/"
*/
public function getBase(): UriInterface
{
return new Uri('/');
}
/**
* Always zero
*/
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
*
* @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
);
}
public function getDefaultLanguage(): SiteLanguage
{
return reset($this->languages);
}
/**
* This takes page TSconfig into account (unlike Site interface) to find
* mod.SHARED.disableLanguages and mod.SHARED.defaultLanguageLabel
*/
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' => 'flags-multiple',
]);
}
$pageTs = BackendUtility::getPagesTSconfig((int)$pageId);
$pageTs = $pageTs['mod.']['SHARED.'] ?? [];
$disabledLanguages = GeneralUtility::intExplode(',', (string)($pageTs['disableLanguages'] ?? ''), true);
// Do not add the ones that are not allowed by the user
foreach ($this->languages as $language) {
if ($user->checkLanguageAccess($language) && !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
*/
public function getErrorHandler(int $statusCode): PageErrorHandlerInterface
{
throw new \RuntimeException('No error handler given for the status code "' . $statusCode . '".', 1522495102);
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
+435
View File
@@ -0,0 +1,435 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Site\Entity;
use Psr\Http\Message\UriInterface;
use Symfony\Component\ExpressionLanguage\SyntaxError;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Error\PageErrorHandler\FluidPageErrorHandler;
use TYPO3\CMS\Core\Error\PageErrorHandler\InvalidPageErrorHandlerException;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
use TYPO3\CMS\Core\Error\PageErrorHandler\RedirectLoginErrorHandler;
use TYPO3\CMS\Core\ExpressionLanguage\Resolver;
use TYPO3\CMS\Core\Http\Uri;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Routing\PageRouter;
use TYPO3\CMS\Core\Routing\RouterInterface;
use TYPO3\CMS\Core\Site\Set\SetError;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Entity representing a single site with available languages
*
* @phpstan-type LanguageRef -1|0|positive-int
*/
class Site implements SiteInterface
{
protected const ERRORHANDLER_TYPE_PAGE = 'Page';
protected const ERRORHANDLER_TYPE_FLUID = 'Fluid';
protected const ERRORHANDLER_TYPE_PHP = 'PHP';
protected const ERRORHANDLER_TYPE_LOGIN_REDIRECT = 'LoginRedirect';
/**
* @var string
*/
protected $identifier;
/**
* @var UriInterface
*/
protected $base;
/**
* @var int
*/
protected $rootPageId;
/**
* Any attributes for this site
* @var array
*/
protected $configuration;
/**
* Raw attributes for this site
* @var array
*/
protected $rawConfiguration;
/**
* @var array<LanguageRef, SiteLanguage>
*/
protected $languages;
/**
* @var list<string>
*/
protected array $sets;
/**
* @var array<string, array{error: SetError, name: string, context: string}>
*/
public array $invalidSets = [];
/**
* @var array
*/
protected $errorHandlers;
protected SiteSettings $settings;
protected ?SiteTypoScript $typoscript;
protected ?SiteTSconfig $tsConfig;
/**
* Sets up a site object, and its languages, error handlers and the settings
*/
public function __construct(string $identifier, int $rootPageId, array $configuration, ?SiteSettings $settings = null, ?SiteTypoScript $typoscript = null, ?SiteTSconfig $tsConfig = null)
{
$this->identifier = $identifier;
$this->rootPageId = $rootPageId;
if ($settings === null) {
// @todo deprecate null settings argument
$settings = SiteSettings::createFromSettingsTree($configuration['settings'] ?? []);
}
$this->settings = $settings;
$this->typoscript = $typoscript;
$this->tsConfig = $tsConfig;
$this->rawConfiguration = $configuration;
// Merge settings back in configuration for backwards-compatibility
$configuration['settings'] = $this->settings->getAll();
$this->configuration = $configuration;
$configuration['languages'] = !empty($configuration['languages']) ? $configuration['languages'] : [
0 => [
'languageId' => 0,
'title' => 'Default',
'navigationTitle' => '',
'flag' => 'us',
'locale' => 'en_US.UTF-8',
],
];
$baseUrl = $this->resolveBaseWithVariants(
$configuration['base'] ?? '',
$configuration['baseVariants'] ?? null
);
$this->base = new Uri($this->sanitizeBaseUrl($baseUrl));
$this->sets = $configuration['dependencies'] ?? [];
foreach ($configuration['languages'] as $languageConfiguration) {
$languageUid = (int)$languageConfiguration['languageId'];
// site language has defined its own base, this is the case most of the time.
if (!empty($languageConfiguration['base'])) {
$base = $this->resolveBaseWithVariants(
$languageConfiguration['base'],
$languageConfiguration['baseVariants'] ?? null
);
$base = new Uri($this->sanitizeBaseUrl($base));
// no host given by the language-specific base, so lets prefix the main site base
if ($base->getScheme() === '' && $base->getHost() === '') {
$base = rtrim((string)$this->base, '/') . '/' . ltrim((string)$base, '/');
$base = new Uri($this->sanitizeBaseUrl($base));
}
} else {
// Language configuration does not have a base defined
// So the main site base is used (usually done for default languages)
$base = new Uri($this->sanitizeBaseUrl(rtrim((string)$this->base, '/') . '/'));
}
if (!empty($languageConfiguration['flag'])) {
if ($languageConfiguration['flag'] === 'global') {
$languageConfiguration['flag'] = 'flags-multiple';
} elseif ($languageConfiguration['flag'] !== 'empty-empty') {
$languageConfiguration['flag'] = 'flags-' . $languageConfiguration['flag'];
}
}
$this->languages[$languageUid] = new SiteLanguage(
$languageUid,
$languageConfiguration['locale'],
$base,
$languageConfiguration
);
}
foreach ($configuration['errorHandling'] ?? [] as $errorHandlingConfiguration) {
$code = $errorHandlingConfiguration['errorCode'];
unset($errorHandlingConfiguration['errorCode']);
$this->errorHandlers[(int)$code] = $errorHandlingConfiguration;
}
}
/**
* Checks if the base has variants, and takes the first variant which matches an expression.
*/
protected function resolveBaseWithVariants(string $baseUrl, ?array $baseVariants): string
{
if (!empty($baseVariants)) {
$expressionLanguageResolver = GeneralUtility::makeInstance(
Resolver::class,
'site',
[]
);
foreach ($baseVariants as $baseVariant) {
try {
if ((bool)$expressionLanguageResolver->evaluate($baseVariant['condition'])) {
$baseUrl = $baseVariant['base'];
break;
}
} catch (SyntaxError $e) {
// silently fail and do not evaluate
// no logger here, as Site is currently cached and serialized
}
}
}
return $baseUrl;
}
/**
* Gets the identifier of this site,
* mainly used when maintaining / configuring sites.
*/
public function getIdentifier(): string
{
return $this->identifier;
}
/**
* Returns the base URL of this site
*/
public function getBase(): UriInterface
{
return $this->base;
}
/**
* Returns the root page ID of this site
*/
public function getRootPageId(): int
{
return $this->rootPageId;
}
/**
* Returns all available languages of this site
*
* @return array<LanguageRef, SiteLanguage>
*/
public function getLanguages(): array
{
$languages = [];
foreach ($this->languages as $languageId => $language) {
if ($language->enabled()) {
$languages[$languageId] = $language;
}
}
return $languages;
}
/**
* Returns configured sets of this site
*
* @return list<string>
*/
public function getSets(): array
{
return $this->sets;
}
/**
* Returns all available languages of this site, even the ones disabled for frontend usages
*
* @return array<LanguageRef, SiteLanguage>
*/
public function getAllLanguages(): array
{
return $this->languages;
}
/**
* Returns a language of this site, given by the sys_language_uid
*
* @throws \InvalidArgumentException
*/
public function getLanguageById(int $languageId): SiteLanguage
{
if (isset($this->languages[$languageId])) {
return $this->languages[$languageId];
}
// @todo: Turn this into a specific exception to avoid catching \InvalidArgumentException
// since there is no hasLanguageById() or similar and some core places already
// call this method and try-catch global \InvalidArgumentException, which is bad practice.
throw new \InvalidArgumentException(
'Language ' . $languageId . ' does not exist on site ' . $this->identifier . '.',
1522960188
);
}
public function getDefaultLanguage(): SiteLanguage
{
foreach ($this->languages as $language) {
if ($language->isPrimary()) {
return $language;
}
}
return reset($this->languages);
}
/**
* @return array<LanguageRef, SiteLanguage>
*/
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' => 'flags-multiple',
]);
}
// Do not add the ones that are not allowed by the user
foreach ($this->languages as $language) {
if ($user->checkLanguageAccess($language)) {
$availableLanguages[$language->getLanguageId()] = $language;
}
}
return $availableLanguages;
}
/**
* Returns a ready-to-use error handler, to be used within the ErrorController
*
* @throws PageErrorHandlerNotConfiguredException
* @throws InvalidPageErrorHandlerException
*/
public function getErrorHandler(int $statusCode): PageErrorHandlerInterface
{
$errorHandlerConfiguration = $this->errorHandlers[$statusCode] ?? $this->errorHandlers[0] ?? null;
switch ($errorHandlerConfiguration['errorHandler'] ?? null) {
case self::ERRORHANDLER_TYPE_FLUID:
return GeneralUtility::makeInstance(FluidPageErrorHandler::class, $statusCode, $errorHandlerConfiguration);
case self::ERRORHANDLER_TYPE_PAGE:
return GeneralUtility::makeInstance(PageContentErrorHandler::class, $statusCode, $errorHandlerConfiguration);
case self::ERRORHANDLER_TYPE_LOGIN_REDIRECT:
return GeneralUtility::makeInstance(RedirectLoginErrorHandler::class, $statusCode, $errorHandlerConfiguration);
case self::ERRORHANDLER_TYPE_PHP:
$handler = GeneralUtility::makeInstance($errorHandlerConfiguration['errorPhpClassFQCN'], $statusCode, $errorHandlerConfiguration);
// Check if the interface is implemented
if (!($handler instanceof PageErrorHandlerInterface)) {
throw new InvalidPageErrorHandlerException('The configured error handler "' . (string)$errorHandlerConfiguration['errorPhpClassFQCN'] . '" for status code ' . $statusCode . ' must implement the PageErrorHandlerInterface.', 1527432330);
}
return $handler;
}
throw new PageErrorHandlerNotConfiguredException('No error handler given for the status code "' . $statusCode . '".', 1522495914);
}
/**
* Returns the whole configuration for this site
*/
public function getConfiguration(): array
{
return $this->configuration;
}
public function getRawConfiguration(): array
{
return $this->rawConfiguration;
}
public function getSettings(): SiteSettings
{
return $this->settings;
}
/**
* @internal
*/
public function isTypoScriptRoot(): bool
{
return $this->sets !== [] || $this->typoscript !== null || $this->tsConfig !== null;
}
public function getTypoScript(): ?SiteTypoScript
{
return $this->typoscript;
}
/**
* @internal
*/
public function getTSconfig(): ?SiteTSconfig
{
return $this->tsConfig;
}
/**
* Returns a single configuration attribute
*
* @return mixed
* @throws \InvalidArgumentException
*/
public function getAttribute(string $attributeName)
{
if (isset($this->configuration[$attributeName])) {
return $this->configuration[$attributeName];
}
throw new \InvalidArgumentException(
'Attribute ' . $attributeName . ' does not exist on site ' . $this->identifier . '.',
1522495954
);
}
/**
* If a site base contains "/" or "www.domain.com", it is ensured that
* parse_url() can handle this kind of configuration properly.
*/
protected function sanitizeBaseUrl(string $base): string
{
// no protocol ("//") and the first part is no "/" (path), means that this is a domain like
// "www.domain.com/subpage", and we want to ensure that this one then gets a "no-scheme agnostic" part
if (!empty($base) && !str_contains($base, '//') && $base[0] !== '/') {
// either a scheme is added, or no scheme but with domain, or a path which is not absolute
// make the base prefixed with a slash, so it is recognized as path, not as domain
// treat as path
if (!str_contains($base, '.')) {
$base = '/' . $base;
} else {
// treat as domain name
$base = '//' . $base;
}
}
return $base;
}
/**
* Returns the applicable router for this site. This might be configurable in the future.
*/
public function getRouter(?Context $context = null): RouterInterface
{
return GeneralUtility::makeInstance(PageRouter::class, $this, $context);
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Site\Entity;
use Psr\Http\Message\UriInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
interface SiteInterface
{
/**
* Returns the root page ID of this site
*/
public function getRootPageId(): int;
/**
* Returns an identifier for the site / configuration
*/
public function getIdentifier(): string;
/**
* Returns the base URL
*/
public function getBase(): UriInterface;
/**
* Returns all available languages of this site visible in the frontend
*
* @return SiteLanguage[]
*/
public function getLanguages(): array;
/**
* Returns a language of this site, given by the sys_language_uid
*
* @throws \InvalidArgumentException
*/
public function getLanguageById(int $languageId): SiteLanguage;
/**
* Returns the first language that was configured. This is usually language=0
*/
public function getDefaultLanguage(): SiteLanguage;
/**
* Fetch the available languages for a specific backend user, used in various places in Backend and Frontend
* when a Backend User is authenticated.
*
* @param BackendUserAuthentication $user the authenticated backend user to check access rights
* @param bool $includeAllLanguagesFlag whether "-1" should be included in the values or not.
* @param int|null $pageId usually used for resolving additional information from PageTS, only used for pseudo-sites. uid of the default language row!
* @return SiteLanguage[]
*/
public function getAvailableLanguages(BackendUserAuthentication $user, bool $includeAllLanguagesFlag = false, ?int $pageId = null): array;
/**
* Returns a ready-to-use error handler, to be used within the ErrorController
*
* @throws PageErrorHandlerNotConfiguredException
*/
public function getErrorHandler(int $statusCode): PageErrorHandlerInterface;
}
+311
View File
@@ -0,0 +1,311 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Site\Entity;
use Psr\Http\Message\UriInterface;
use TYPO3\CMS\Core\Localization\Locale;
/**
* Entity representing a site_language configuration of a site object.
*/
class SiteLanguage
{
/**
* The language id.
*
* @var int
*/
protected $languageId;
/**
* The language tag, like 'de', 'en', 'de-CH'.
*/
protected string $languageTag = '';
/**
* Locale, like 'de-CH' or 'en-GB'
*/
protected Locale $locale;
/**
* The Base URL for this language
*
* @var UriInterface
*/
protected $base;
/**
* Label to be used within TYPO3 to identify the language
* @var string
*/
protected $title = 'Default';
/**
* Label to be used within language menus
* @var string
*/
protected $navigationTitle = '';
/**
* Localized title of the site to be used in title tag.
* @var string
*/
protected $websiteTitle = '';
/**
* The flag key (like "gb" or "fr") used to be used in TYPO3's Backend.
* @var string
*/
protected $flagIdentifier = '';
/**
* Language tag for this language defined by RFC 1766 / 3066 for "hreflang" attribute
*
* @var string
*/
protected $hreflang = '';
/**
* Prefix for TYPO3's language files. If empty, this
* is fetched from $locale
*
* @var string
*/
protected $typo3Language = '';
/**
* @var string
*/
protected $fallbackType = 'strict';
/**
* @var array
*/
protected $fallbackLanguageIds = [];
/**
* @var bool
*/
protected $enabled = true;
/**
* Whether this language is the primary language of the site.
*/
protected bool $primary = false;
/**
* Additional parameters configured for this site language
* @var array
*/
protected $configuration = [];
/**
* SiteLanguage constructor.
*/
public function __construct(int $languageId, string $locale, UriInterface $base, array $configuration)
{
$this->languageId = $languageId;
$this->locale = new Locale($locale);
$this->base = $base;
$this->configuration = $configuration;
if (!empty($configuration['languageTag'])) {
$this->languageTag = $configuration['languageTag'];
}
if (!empty($configuration['title'])) {
$this->title = $configuration['title'];
}
if (!empty($configuration['navigationTitle'])) {
$this->navigationTitle = $configuration['navigationTitle'];
}
if (!empty($configuration['websiteTitle'])) {
$this->websiteTitle = $configuration['websiteTitle'];
}
if (!empty($configuration['flag'])) {
$this->flagIdentifier = $configuration['flag'];
}
if (!empty($configuration['typo3Language'])) {
$this->typo3Language = $configuration['typo3Language'];
}
if (!empty($configuration['hreflang'])) {
$this->hreflang = $configuration['hreflang'];
}
if (!empty($configuration['fallbackType'])) {
$this->fallbackType = $configuration['fallbackType'];
}
if (isset($configuration['fallbacks'])) {
$fallbackLanguageIds = $configuration['fallbacks'];
// It is important to distinct between "0" and "" so, empty() should not be used here
if (is_string($fallbackLanguageIds)) {
if ($fallbackLanguageIds !== '') {
$fallbackLanguageIds = explode(',', $fallbackLanguageIds);
} else {
$fallbackLanguageIds = [];
}
} elseif (is_scalar($fallbackLanguageIds)) {
$fallbackLanguageIds = [$fallbackLanguageIds];
}
$this->fallbackLanguageIds = array_map(intval(...), $fallbackLanguageIds);
}
if (isset($configuration['enabled'])) {
$this->enabled = (bool)$configuration['enabled'];
}
if (!empty($configuration['primary'])) {
$this->primary = true;
}
}
/**
* Returns the SiteLanguage in an array representation for e.g. the usage
* in TypoScript.
*/
public function toArray(): array
{
return array_merge($this->configuration, [
'languageId' => $this->getLanguageId(),
'languageTag' => $this->getLanguageTag(),
// kept for backwards-compat for the time being, might change to BGP-47 format
'locale' => $this->getLocale()->posixFormatted(),
'base' => (string)$this->getBase(),
'title' => $this->getTitle(),
'websiteTitle' => $this->getWebsiteTitle(),
'navigationTitle' => $this->getNavigationTitle(),
'hreflang' => $this->hreflang ?: $this->locale->getName(),
'typo3Language' => $this->getTypo3Language(),
'flagIdentifier' => $this->getFlagIdentifier(),
'fallbackType' => $this->getFallbackType(),
'enabled' => $this->enabled(),
'primary' => $this->isPrimary(),
'fallbackLanguageIds' => $this->getFallbackLanguageIds(),
]);
}
public function getConfiguration(): array
{
return $this->toArray();
}
public function getLanguageId(): int
{
return $this->languageId;
}
public function getLanguageTag(): string
{
return $this->languageTag;
}
public function getLocale(): Locale
{
return $this->locale;
}
public function getBase(): UriInterface
{
return $this->base;
}
public function getTitle(): string
{
return $this->title;
}
public function getNavigationTitle(): string
{
return $this->navigationTitle ?: $this->title;
}
public function getWebsiteTitle(): string
{
return $this->websiteTitle;
}
public function getFlagIdentifier(): string
{
return $this->flagIdentifier;
}
/**
* Returns the XLF label language key.
* For locales like "en-US", this method returns "en_US" which can then be used
* for XLF file prefixes properly.
*/
public function getTypo3Language(): string
{
if ($this->typo3Language !== '') {
return $this->typo3Language;
}
$typo3Language = $this->locale->getLanguageCode();
if ($this->locale->getCountryCode()) {
$typo3Language .= '_' . $this->locale->getCountryCode();
}
return $typo3Language;
}
/**
* @internal
*/
public function hasCustomTypo3Language(): bool
{
return $this->typo3Language !== '';
}
/**
* Returns the RFC 1766 / 3066 language tag for hreflang tags
*/
public function getHreflang(bool $fetchCustomSetting = false): string
{
// Ensure to check if a custom attribute is set
if ($fetchCustomSetting) {
return $this->hreflang;
}
return $this->hreflang ?: $this->locale->getName();
}
/**
* Returns true if the language is available in frontend usage
*/
public function enabled(): bool
{
return $this->enabled;
}
/**
* Helper so fluid can work with this as well.
*/
public function isEnabled(): bool
{
return $this->enabled;
}
public function isPrimary(): bool
{
return $this->primary;
}
public function getFallbackType(): string
{
return $this->fallbackType;
}
public function getFallbackLanguageIds(): array
{
return $this->fallbackLanguageIds;
}
}
+134
View File
@@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Site\Entity;
use TYPO3\CMS\Core\Settings\Settings;
use TYPO3\CMS\Core\Settings\SettingsInterface;
use TYPO3\CMS\Core\Utility\ArrayUtility;
/**
* Entity representing all settings for a site. These settings are not overlaid
* with TypoScript settings / constants which happens in the TypoScript Parser
* for a specific page.
*/
final readonly class SiteSettings implements SettingsInterface, \JsonSerializable
{
/**
* @internal to be constructed by create() or createFromSettingsTree()
*/
public function __construct(
private SettingsInterface $settings,
private array $settingsTree,
private array $flattenedArrayValues,
) {}
public function has(string $identifier): bool
{
return $this->settings->has($identifier) || array_key_exists($identifier, $this->settingsTree) || array_key_exists($identifier, $this->flattenedArrayValues);
}
public function isEmpty(): bool
{
return $this->settings->getIdentifiers() === [];
}
public function get(string $identifier, mixed $defaultValue = null): mixed
{
if ($this->settings->has($identifier)) {
return $this->settings->get($identifier);
}
return $this->flattenedArrayValues[$identifier] ?? $this->settingsTree[$identifier] ?? $defaultValue;
}
public function getAll(): array
{
return $this->settingsTree;
}
public function getMap(): array
{
$map = [];
foreach ($this->settings->getIdentifiers() as $key) {
$map[$key] = $this->settings->get($key);
}
return $map;
}
public function getAllFlat(): array
{
return [
...$this->flattenedArrayValues,
...array_filter($this->getMap(), static fn(mixed $value): bool => !is_array($value)),
];
}
/**
* @todo Update jsonSerialize() to return settings map and settings tree values, or remove altogether.
*/
public function jsonSerialize(): mixed
{
return json_encode($this->settingsTree);
}
public function getIdentifiers(): array
{
return $this->settings->getIdentifiers();
}
public static function __set_state(array $state): static
{
return new static(...$state);
}
/**
* @internal
*/
public static function create(SettingsInterface $settings): self
{
$tree = [];
$flattenedArrayValues = [];
foreach ($settings->getIdentifiers() as $key) {
$value = $settings->get($key);
$tree = ArrayUtility::setValueByPath($tree, $key, $value, '.');
if (is_array($value)) {
foreach (ArrayUtility::flattenPlain($value) as $flatKey => $flatValue) {
$flattenedArrayValues[$key . '.' . $flatKey] = $flatValue;
}
}
}
return new self(
settings: $settings,
settingsTree: $tree,
flattenedArrayValues: $flattenedArrayValues,
);
}
/**
* @internal
*/
public static function createFromSettingsTree(array $settingsTree): self
{
$flatSettings = $settingsTree === [] ? [] : ArrayUtility::flattenPlain($settingsTree);
return new self(
settings: new Settings($flatSettings),
settingsTree: $settingsTree,
flattenedArrayValues: [],
);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Site\Entity;
/**
* @internal
*/
final readonly class SiteTSconfig
{
public function __construct(
public ?string $pageTSconfig = null,
) {}
public static function __set_state(array $state): self
{
return new self(...$state);
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Site\Entity;
final readonly class SiteTypoScript
{
public function __construct(
public ?string $setup = null,
public ?string $constants = null,
) {}
public static function __set_state(array $state): self
{
return new self(...$state);
}
}