‪TYPO3CMS  10.4
SiteConfiguration.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Symfony\Component\Finder\Finder;
21 use Symfony\Component\Yaml\Yaml;
32 
41 {
45  protected ‪$configPath;
46 
53  protected ‪$configFileName = 'config.yaml';
54 
61  protected ‪$cacheIdentifier = 'sites-configuration';
62 
69  protected ‪$firstLevelCache;
70 
74  public function ‪__construct(string ‪$configPath)
75  {
76  $this->configPath = ‪$configPath;
77  }
78 
85  public function ‪getAllExistingSites(bool $useCache = true): array
86  {
87  if ($useCache && $this->firstLevelCache !== null) {
89  }
90  return $this->‪resolveAllExistingSites($useCache);
91  }
92 
100  public function ‪createNewBasicSite(string $identifier, int $rootPageId, string $base): void
101  {
102  // Create a default site configuration called "main" as best practice
103  $this->‪write($identifier, [
104  'rootPageId' => $rootPageId,
105  'base' => $base,
106  'languages' => [
107  0 => [
108  'title' => 'English',
109  'enabled' => true,
110  'languageId' => 0,
111  'base' => '/',
112  'typo3Language' => 'default',
113  'locale' => 'en_US.UTF-8',
114  'iso-639-1' => 'en',
115  'navigationTitle' => 'English',
116  'hreflang' => 'en-us',
117  'direction' => 'ltr',
118  'flag' => 'us',
119  ],
120  ],
121  'errorHandling' => [],
122  'routes' => [],
123  ]);
124  }
125 
132  public function ‪resolveAllExistingSites(bool $useCache = true): array
133  {
134  $sites = [];
135  $siteConfiguration = $this->‪getAllSiteConfigurationFromFiles($useCache);
136  foreach ($siteConfiguration as $identifier => $configuration) {
137  $rootPageId = (int)($configuration['rootPageId'] ?? 0);
138  if ($rootPageId > 0) {
139  $sites[$identifier] = GeneralUtility::makeInstance(Site::class, $identifier, $rootPageId, $configuration);
140  }
141  }
142  $this->firstLevelCache = $sites;
143  return $sites;
144  }
145 
153  protected function ‪getAllSiteConfigurationFromFiles(bool $useCache = true): array
154  {
155  // Check if the data is already cached
156  $siteConfiguration = $useCache ? $this->‪getCache()->require($this->cacheIdentifier) : false;
157  if ($siteConfiguration !== false) {
158  return $siteConfiguration;
159  }
160  ‪$finder = new Finder();
161  try {
162  ‪$finder->files()->depth(0)->name($this->configFileName)->in($this->configPath . '/*');
163  } catch (\InvalidArgumentException $e) {
164  // Directory $this->configPath does not exist yet
165  ‪$finder = [];
166  }
167  $loader = GeneralUtility::makeInstance(YamlFileLoader::class);
168  $siteConfiguration = [];
169  foreach (‪$finder as $fileInfo) {
170  $configuration = $loader->load(GeneralUtility::fixWindowsFilePath((string)$fileInfo));
171  $identifier = basename($fileInfo->getPath());
172  $siteConfiguration[$identifier] = $configuration;
173  }
174  $this->‪getCache()->set($this->cacheIdentifier, 'return ' . var_export($siteConfiguration, true) . ';');
175 
176  return $siteConfiguration;
177  }
178 
189  public function ‪load(string $siteIdentifier): array
190  {
191  $fileName = $this->configPath . '/' . $siteIdentifier . '/' . ‪$this->configFileName;
192  $loader = GeneralUtility::makeInstance(YamlFileLoader::class);
193  return $loader->load(GeneralUtility::fixWindowsFilePath($fileName), ‪YamlFileLoader::PROCESS_IMPORTS);
194  }
195 
204  public function ‪write(string $siteIdentifier, array $configuration, bool $protectPlaceholders = false): void
205  {
206  $folder = $this->configPath . '/' . $siteIdentifier;
207  $fileName = $folder . '/' . ‪$this->configFileName;
208  $newConfiguration = $configuration;
209  if (!file_exists($folder)) {
211  if ($protectPlaceholders && $newConfiguration !== []) {
212  $newConfiguration = $this->protectPlaceholders([], $newConfiguration);
213  }
214  } elseif (file_exists($fileName)) {
215  $loader = GeneralUtility::makeInstance(YamlFileLoader::class);
216  // load without any processing to have the unprocessed base to modify
217  $newConfiguration = $loader->load(GeneralUtility::fixWindowsFilePath($fileName), 0);
218  // load the processed configuration to diff changed values
219  $processed = $loader->load(GeneralUtility::fixWindowsFilePath($fileName));
220  // find properties that were modified via GUI
221  $newModified = array_replace_recursive(
222  self::findRemoved($processed, $configuration),
223  self::findModified($processed, $configuration)
224  );
225  if ($protectPlaceholders && $newModified !== []) {
226  $newModified = $this->protectPlaceholders($newConfiguration, $newModified);
227  }
228  // change _only_ the modified keys, leave the original non-changed areas alone
229  ‪ArrayUtility::mergeRecursiveWithOverrule($newConfiguration, $newModified);
230  }
231  $newConfiguration = $this->‪sortConfiguration($newConfiguration);
232  $yamlFileContents = Yaml::dump($newConfiguration, 99, 2);
233  ‪GeneralUtility::writeFile($fileName, $yamlFileContents);
234  $this->firstLevelCache = null;
235  $this->‪getCache()->remove($this->cacheIdentifier);
236  }
237 
245  public function ‪rename(string $currentIdentifier, string $newIdentifier): void
246  {
247  $result = ‪rename($this->configPath . '/' . $currentIdentifier, $this->configPath . '/' . $newIdentifier);
248  if (!$result) {
249  throw new \RuntimeException('Unable to rename folder sites/' . $currentIdentifier, 1522491300);
250  }
251  $this->‪getCache()->remove($this->cacheIdentifier);
252  $this->firstLevelCache = null;
253  }
254 
262  public function delete(string $siteIdentifier): void
263  {
264  $sites = $this->‪getAllExistingSites();
265  if (!isset($sites[$siteIdentifier])) {
266  throw new SiteNotFoundException('Site configuration named ' . $siteIdentifier . ' not found.', 1522866183);
267  }
268  $fileName = $this->configPath . '/' . $siteIdentifier . '/' . ‪$this->configFileName;
269  if (!file_exists($fileName)) {
270  throw new SiteNotFoundException('Site configuration file ' . $this->configFileName . ' within the site ' . $siteIdentifier . ' not found.', 1522866184);
271  }
272  @unlink($fileName);
273  $this->‪getCache()->remove($this->cacheIdentifier);
274  $this->firstLevelCache = null;
275  }
276 
286  protected function protectPlaceholders(array $existingConfiguration, array $modifiedConfiguration): array
287  {
288  try {
289  return GeneralUtility::makeInstance(YamlPlaceholderGuard::class, $existingConfiguration)
290  ->process($modifiedConfiguration);
291  } catch (‪YamlPlaceholderException $exception) {
292  throw new \RuntimeException($exception->getMessage(), 1670361271, $exception);
293  }
294  }
295 
302  protected function ‪getCache(): ‪PhpFrontend
303  {
304  return GeneralUtility::makeInstance(CacheManager::class)->getCache('core');
305  }
306 
311  protected function ‪sortConfiguration(array $newConfiguration): array
312  {
313  ksort($newConfiguration);
314  if (isset($newConfiguration['imports'])) {
315  $imports = $newConfiguration['imports'];
316  unset($newConfiguration['imports']);
317  $newConfiguration['imports'] = $imports;
318  }
319  return $newConfiguration;
320  }
321 
322  protected static function ‪findModified(array $currentConfiguration, array $newConfiguration): array
323  {
324  $differences = [];
325  foreach ($newConfiguration as $key => $value) {
326  if (!isset($currentConfiguration[$key]) || $currentConfiguration[$key] !== $newConfiguration[$key]) {
327  if (!isset($newConfiguration[$key]) && isset($currentConfiguration[$key])) {
328  $differences[$key] = '__UNSET';
329  } elseif (isset($currentConfiguration[$key])
330  && is_array($newConfiguration[$key])
331  && is_array($currentConfiguration[$key])
332  ) {
333  $differences[$key] = ‪self::findModified($currentConfiguration[$key], $newConfiguration[$key]);
334  } else {
335  $differences[$key] = $value;
336  }
337  }
338  }
339  return $differences;
340  }
341 
342  protected static function ‪findRemoved(array $currentConfiguration, array $newConfiguration): array
343  {
344  $removed = [];
345  foreach ($currentConfiguration as $key => $value) {
346  if (!isset($newConfiguration[$key])) {
347  $removed[$key] = '__UNSET';
348  } elseif (isset($currentConfiguration[$key]) && is_array($currentConfiguration[$key]) && is_array($newConfiguration[$key])) {
349  $removedInRecursion = ‪self::findRemoved($currentConfiguration[$key], $newConfiguration[$key]);
350  if (!empty($removedInRecursion)) {
351  $removed[$key] = $removedInRecursion;
352  }
353  }
354  }
355 
356  return $removed;
357  }
358 }
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\PROCESS_IMPORTS
‪const PROCESS_IMPORTS
Definition: YamlFileLoader.php:53
‪$finder
‪if(PHP_SAPI !=='cli') $finder
Definition: header-comment.php:22
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\getAllExistingSites
‪Site[] getAllExistingSites(bool $useCache=true)
Definition: SiteConfiguration.php:81
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\findRemoved
‪static findRemoved(array $currentConfiguration, array $newConfiguration)
Definition: SiteConfiguration.php:338
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\getAllSiteConfigurationFromFiles
‪array getAllSiteConfigurationFromFiles(bool $useCache=true)
Definition: SiteConfiguration.php:149
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\rename
‪rename(string $currentIdentifier, string $newIdentifier)
Definition: SiteConfiguration.php:241
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:26
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:654
‪TYPO3\CMS\Core\Configuration\SiteConfiguration
Definition: SiteConfiguration.php:41
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\sortConfiguration
‪array sortConfiguration(array $newConfiguration)
Definition: SiteConfiguration.php:307
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\__construct
‪__construct(string $configPath)
Definition: SiteConfiguration.php:70
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:40
‪TYPO3\CMS\Core\Configuration\Loader\Exception\YamlPlaceholderException
Definition: YamlPlaceholderException.php:21
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\resolveAllExistingSites
‪Site[] resolveAllExistingSites(bool $useCache=true)
Definition: SiteConfiguration.php:128
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:2022
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\$firstLevelCache
‪array null $firstLevelCache
Definition: SiteConfiguration.php:65
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\createNewBasicSite
‪createNewBasicSite(string $identifier, int $rootPageId, string $base)
Definition: SiteConfiguration.php:96
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\$configFileName
‪string $configFileName
Definition: SiteConfiguration.php:51
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:35
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\$configPath
‪string $configPath
Definition: SiteConfiguration.php:44
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\load
‪array load(string $siteIdentifier)
Definition: SiteConfiguration.php:185
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\getCache
‪catch(YamlPlaceholderException $exception) PhpFrontend getCache()
Definition: SiteConfiguration.php:298
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\$cacheIdentifier
‪string $cacheIdentifier
Definition: SiteConfiguration.php:58
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader
Definition: YamlFileLoader.php:48
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:23
‪TYPO3\CMS\Core\Configuration
Definition: ConfigurationManager.php:16
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\findModified
‪static findModified(array $currentConfiguration, array $newConfiguration)
Definition: SiteConfiguration.php:318
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile($file, $content, $changePermissions=false)
Definition: GeneralUtility.php:1836
‪TYPO3\CMS\Core\Configuration\Loader\YamlPlaceholderGuard
Definition: YamlPlaceholderGuard.php:27
‪TYPO3\CMS\Core\Configuration\SiteConfiguration\write
‪write(string $siteIdentifier, array $configuration, bool $protectPlaceholders=false)
Definition: SiteConfiguration.php:200