‪TYPO3CMS  ‪main
TsConfigTreeBuilder.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\DependencyInjection\Attribute\Autoconfigure;
27 use TYPO3\CMS\Core\Package\PackageManager;
39 
46 #[Autoconfigure(public: true)]
48 {
49  public function ‪__construct(
50  private readonly ‪TreeFromLineStreamBuilder $treeFromTokenStreamBuilder,
51  private readonly PackageManager $packageManager,
52  private readonly ‪EventDispatcher $eventDispatcher,
53  private readonly ‪SiteFinder $siteFinder,
54  private readonly ‪SetRegistry $setRegistry,
55  ) {}
56 
57  public function ‪getUserTsConfigTree(
58  ‪BackendUserAuthentication $backendUser,
59  ‪TokenizerInterface $tokenizer,
60  ?‪PhpFrontend $cache = null
61  ): ‪RootInclude {
62  $includeTree = new ‪RootInclude();
63 
64  $collectedUserTsConfigArray = [];
65  $gotPackagesUserTsConfigFromCache = false;
66  $cacheIdentifier = (new ‪PackageDependentCacheIdentifier($this->packageManager))
67  ->withPrefix('usertsconfig-packages-strings')
68  ->toString();
69  if ($cache) {
70  $collectedUserTsConfigArrayFromCache = $cache->require($cacheIdentifier);
71  if ($collectedUserTsConfigArrayFromCache) {
72  $gotPackagesUserTsConfigFromCache = true;
73  $collectedUserTsConfigArray = $collectedUserTsConfigArrayFromCache;
74  }
75  }
76  if (!$gotPackagesUserTsConfigFromCache) {
77  $event = $this->eventDispatcher->dispatch(new ‪BeforeLoadedUserTsConfigEvent());
78  $collectedUserTsConfigArray = $event->getTsConfig();
79  foreach ($this->packageManager->getActivePackages() as $package) {
80  $packagePath = $package->getPackagePath();
81  $tsConfigFile = null;
82  if (file_exists($packagePath . 'Configuration/user.tsconfig')) {
83  $tsConfigFile = $packagePath . 'Configuration/user.tsconfig';
84  } elseif (file_exists($packagePath . 'Configuration/User.tsconfig')) {
85  $tsConfigFile = $packagePath . 'Configuration/User.tsconfig';
86  }
87  if ($tsConfigFile) {
88  $typoScriptString = @file_get_contents($tsConfigFile);
89  if (!empty($typoScriptString)) {
90  $collectedUserTsConfigArray['userTsConfig-package-' . $package->getPackageKey()] = $typoScriptString;
91  }
92  }
93  }
94  $cache?->set($cacheIdentifier, 'return unserialize(\'' . addcslashes(serialize($collectedUserTsConfigArray), '\'\\') . '\');');
95  }
96  foreach ($collectedUserTsConfigArray as $key => $typoScriptString) {
97  $includeTree->addChild($this->‪getTreeFromString((string)$key, $typoScriptString, $tokenizer, $cache));
98  }
99 
100  // @deprecated since TYPO3 v13. Remove in v14 along with defaultUserTSconfig and EMU::addUserTSConfig
101  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'] ?? '')) {
102  $includeTree->addChild($this->‪getTreeFromString('userTsConfig-globals', ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'], $tokenizer, $cache));
103  }
104 
105  foreach ($backendUser->userGroupsUID as $groupId) {
106  // Loop through all groups and add their 'TSconfig' fields
107  if (!empty($backendUser->userGroups[$groupId]['TSconfig'] ?? '')) {
108  $includeTree->addChild($this->getTreeFromString('userTsConfig-group-' . $groupId, $backendUser->userGroups[$groupId]['TSconfig'], $tokenizer, $cache));
109  }
110  }
111  if (!empty($backendUser->user['TSconfig'] ?? '')) {
112  $includeTree->addChild($this->getTreeFromString('userTsConfig-user', $backendUser->user['TSconfig'], $tokenizer, $cache));
113  }
114 
115  return $includeTree;
116  }
117 
118  public function ‪getPagesTsConfigTree(
119  array $rootLine,
120  ‪TokenizerInterface $tokenizer,
121  ?‪PhpFrontend $cache = null
122  ): ‪RootInclude {
123  $collectedPagesTsConfigArray = [];
124 
125  $collectedPagesTsConfigArray += $this->getPackagePageTsConfigTree($cache);
126 
127  // @deprecated since TYPO3 v13. Remove in v14 along with defaultPageTSconfig and EMU::addPageTSConfig
128  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'] ?? '')) {
129  $collectedPagesTsConfigArray['pagesTsConfig-globals-defaultPageTSconfig'] = ['content' => ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig']];
130  }
131 
132  // HEADS up: rootLine may be modified by getSitePagesTsConfigTree
133  $collectedPagesTsConfigArray += $this->getSitePageTsConfigTree($rootLine, $cache);
134 
135  $collectedPagesTsConfigArray += $this->getRootlinePageTsConfigTree($rootLine, $cache);
136 
137  $event = $this->eventDispatcher->dispatch(new ‪ModifyLoadedPageTsConfigEvent(
138  array_map(static fn(array $descriptor): string => $descriptor['content'], $collectedPagesTsConfigArray),
139  $rootLine
140  ));
141  $collectedPagesTsConfigContentArray = $event->getTsConfig();
142  foreach ($collectedPagesTsConfigContentArray as $key => $content) {
143  $collectedPagesTsConfigArray[$key]['content'] = $content;
144  }
145 
146  $includeTree = new ‪RootInclude();
147  foreach ($collectedPagesTsConfigArray as $key => $descriptor) {
148  $typoScriptString = $descriptor['content'];
149  $filename = $descriptor['filename'] ?? null;
150  $includeTree->addChild($this->getTreeFromString((string)$key, $typoScriptString, $tokenizer, $cache, $filename));
151  }
152  return $includeTree;
153  }
154 
156  ?‪PhpFrontend $cache = null
157  ): array {
158  $collectedPagesTsConfigArray = [];
159  $gotPackagesPagesTsConfigFromCache = false;
160  $cacheIdentifier = (new ‪PackageDependentCacheIdentifier($this->packageManager))
161  ->withPrefix('pagestsconfig-packages-strings')
162  ->toString();
163  if ($cache) {
164  $collectedPagesTsConfigArrayFromCache = $cache->require($cacheIdentifier);
165  if ($collectedPagesTsConfigArrayFromCache) {
166  $gotPackagesPagesTsConfigFromCache = true;
167  $collectedPagesTsConfigArray = $collectedPagesTsConfigArrayFromCache;
168  }
169  }
170  if (!$gotPackagesPagesTsConfigFromCache) {
171  $event = $this->eventDispatcher->dispatch(new ‪BeforeLoadedPageTsConfigEvent());
172  $collectedPagesTsConfigArray = array_map(static fn(string $config): array => ['content' => $config, 'filename' => null], $event->getTsConfig());
173  foreach ($this->packageManager->getActivePackages() as $package) {
174  $packagePath = $package->getPackagePath();
175  $tsConfigFile = null;
176  if (file_exists($packagePath . 'Configuration/page.tsconfig')) {
177  $tsConfigFile = $packagePath . 'Configuration/page.tsconfig';
178  } elseif (file_exists($packagePath . 'Configuration/Page.tsconfig')) {
179  $tsConfigFile = $packagePath . 'Configuration/Page.tsconfig';
180  }
181  if ($tsConfigFile) {
182  $typoScriptString = @file_get_contents($tsConfigFile);
183  if (!empty($typoScriptString)) {
184  $collectedPagesTsConfigArray['pagesTsConfig-package-' . $package->getPackageKey()] = [
185  'filename' => $tsConfigFile,
186  'content' => $typoScriptString,
187  ];
188  }
189  }
190  }
191  $cache?->set($cacheIdentifier, 'return unserialize(\'' . addcslashes(serialize($collectedPagesTsConfigArray), '\'\\') . '\');');
192  }
193  return $collectedPagesTsConfigArray;
194  }
195 
196  private function ‪getSitePageTsConfigTree(
197  array &$rootLine,
198  ?‪PhpFrontend $cache = null
199  ): array {
200  $reverseRootLine = array_reverse($rootLine);
201  $rootlineUntilSite = [];
202  $rootSite = null;
203  foreach ($reverseRootLine as $rootLineEntry) {
204  array_unshift($rootlineUntilSite, $rootLineEntry);
205  ‪$uid = (int)($rootLineEntry['uid'] ?? 0);
206  if (‪$uid === 0) {
207  continue;
208  }
209  try {
210  $site = $this->siteFinder->getSiteByRootPageId(‪$uid);
211  } catch (‪SiteNotFoundException) {
212  continue;
213  }
214  if ($site->isTypoScriptRoot()) {
215  $rootSite = $site;
216  $rootLine = $rootlineUntilSite;
217  break;
218  }
219  }
220 
221  if ($rootSite === null) {
222  return [];
223  }
224 
225  $cacheIdentifier = (new ‪PackageDependentCacheIdentifier($this->packageManager))
226  ->withPrefix('pagestsconfig-site')
227  ->withAdditionalHashedIdentifier($rootSite->getIdentifier())
228  ->toString();
229  $pageTsConfig = $cache?->require($cacheIdentifier) ?: null;
230 
231  if ($pageTsConfig === null) {
232  $pageTsConfig = [];
233  $sets = $this->setRegistry->getSets(...$rootSite->getSets());
234  foreach ($sets as $set) {
235  if ($set->pagets !== null && file_exists($set->pagets)) {
236  $content = @file_get_contents($set->pagets);
237  if (!empty($content)) {
238  $pageTsConfig['pageTsConfig-set-' . str_replace('/', '-', $set->name)] = [
239  'filename' => GeneralUtility::getFileAbsFileName($set->pagets),
240  'content' => $content,
241  ];
242  }
243  }
244  }
245 
246  $pageTsConfig['pageTsConfig-site-' . $rootSite->getIdentifier()] = [
247  'filename' => GeneralUtility::getFileAbsFileName(‪Environment::getConfigPath() . '/sites/' . $rootSite->getIdentifier() . '/page.tsconfig'),
248  'content' => $rootSite->getTSconfig()->pageTSconfig ?? '',
249  ];
250  $cache?->set($cacheIdentifier, 'return ' . var_export($pageTsConfig, true) . ';');
251  }
252  return $pageTsConfig;
253  }
254 
256  array $rootLine,
257  ?‪PhpFrontend $cache = null
258  ): array {
259  $collectedPagesTsConfigArray = [];
260  foreach ($rootLine as $page) {
261  if (empty($page['uid'])) {
262  // Page 0 can happen when the rootline is given from BE context. It has not TSconfig. Skip this.
263  continue;
264  }
265  if (trim($page['tsconfig_includes'] ?? '')) {
266  $includeTsConfigFileList = ‪GeneralUtility::trimExplode(',', $page['tsconfig_includes'], true);
267  foreach ($includeTsConfigFileList as $key => $includeTsConfigFile) {
268  if (‪PathUtility::isExtensionPath($includeTsConfigFile)) {
269  [$includeTsConfigFileExtensionKey, $includeTsConfigFilename] = explode('/', substr($includeTsConfigFile, 4), 2);
270  if ($includeTsConfigFileExtensionKey !== ''
271  && ‪ExtensionManagementUtility::isLoaded($includeTsConfigFileExtensionKey)
272  && $includeTsConfigFilename !== ''
273  ) {
274  $extensionPath = ‪ExtensionManagementUtility::extPath($includeTsConfigFileExtensionKey);
275  $includeTsConfigFileAndPath = ‪PathUtility::getCanonicalPath($extensionPath . $includeTsConfigFilename);
276  if (str_starts_with($includeTsConfigFileAndPath, $extensionPath) && file_exists($includeTsConfigFileAndPath)) {
277  $typoScriptString = (string)file_get_contents($includeTsConfigFileAndPath);
278  if (!empty($typoScriptString)) {
279  $collectedPagesTsConfigArray['pagesTsConfig-page-' . $page['uid'] . '-includes-' . $key] = [
280  'content' => (string)file_get_contents($includeTsConfigFileAndPath),
281  ];
282  }
283  }
284  }
285  }
286  }
287  }
288  if (!empty($page['TSconfig'])) {
289  $collectedPagesTsConfigArray['pagesTsConfig-page-' . $page['uid'] . '-tsConfig'] = ['content' => $page['TSconfig']];
290  }
291  }
292  return $collectedPagesTsConfigArray;
293  }
294 
295  private function ‪getTreeFromString(
296  string ‪$name,
297  string $typoScriptString,
298  ‪TokenizerInterface $tokenizer,
299  ?‪PhpFrontend $cache = null,
300  ?string $filename = null,
301  ): ‪TsConfigInclude {
302  $lowercaseName = mb_strtolower(‪$name);
303  ‪$identifier = (new ‪PackageDependentCacheIdentifier($this->packageManager))
304  ->withPrefix($lowercaseName)
305  ->withAdditionalHashedIdentifier($typoScriptString)
306  ->toString();
307  if ($cache) {
308  $includeNode = $cache->require(‪$identifier);
309  if ($includeNode instanceof ‪TsConfigInclude) {
310  return $includeNode;
311  }
312  }
313  $includeNode = new ‪TsConfigInclude();
314  $includeNode->setName(‪$name);
315  if ($filename !== null) {
316  $includeNode->setPath($filename);
317  }
318  $includeNode->setLineStream($tokenizer->‪tokenize($typoScriptString));
319  $this->treeFromTokenStreamBuilder->buildTree($includeNode, 'tsconfig', $tokenizer);
320  $cache?->set(‪$identifier, 'return unserialize(\'' . addcslashes(serialize($includeNode), '\'\\') . '\');');
321  return $includeNode;
322  }
323 }
‪TYPO3\CMS\Core\Utility\PathUtility\getCanonicalPath
‪static string getCanonicalPath(string $path)
Definition: PathUtility.php:365
‪TYPO3\CMS\Core\Utility\PathUtility\isExtensionPath
‪static isExtensionPath(string $path)
Definition: PathUtility.php:118
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder\getTreeFromString
‪getTreeFromString(string $name, string $typoScriptString, TokenizerInterface $tokenizer, ?PhpFrontend $cache=null, ?string $filename=null,)
Definition: TsConfigTreeBuilder.php:295
‪TYPO3\CMS\Core\TypoScript\IncludeTree
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Core\Package\Cache\PackageDependentCacheIdentifier
Definition: PackageDependentCacheIdentifier.php:30
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder\getUserTsConfigTree
‪getUserTsConfigTree(BackendUserAuthentication $backendUser, TokenizerInterface $tokenizer, ?PhpFrontend $cache=null)
Definition: TsConfigTreeBuilder.php:57
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TreeFromLineStreamBuilder
Definition: TreeFromLineStreamBuilder.php:59
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static isLoaded(string $key)
Definition: ExtensionManagementUtility.php:55
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder\getRootlinePageTsConfigTree
‪getRootlinePageTsConfigTree(array $rootLine, ?PhpFrontend $cache=null)
Definition: TsConfigTreeBuilder.php:255
‪TYPO3\CMS\Core\Core\Environment\getConfigPath
‪static getConfigPath()
Definition: Environment.php:212
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:32
‪TYPO3\CMS\Core\Site\Set\SetRegistry
Definition: SetRegistry.php:30
‪TYPO3\CMS\Core\EventDispatcher\EventDispatcher
Definition: EventDispatcher.php:30
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder\__construct
‪__construct(private readonly TreeFromLineStreamBuilder $treeFromTokenStreamBuilder, private readonly PackageManager $packageManager, private readonly EventDispatcher $eventDispatcher, private readonly SiteFinder $siteFinder, private readonly SetRegistry $setRegistry,)
Definition: TsConfigTreeBuilder.php:49
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\extPath
‪static extPath(string $key, string $script='')
Definition: ExtensionManagementUtility.php:82
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder\getPagesTsConfigTree
‪getPagesTsConfigTree(array $rootLine, TokenizerInterface $tokenizer, ?PhpFrontend $cache=null)
Definition: TsConfigTreeBuilder.php:118
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder\getSitePageTsConfigTree
‪getSitePageTsConfigTree(array &$rootLine, ?PhpFrontend $cache=null)
Definition: TsConfigTreeBuilder.php:196
‪TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\TsConfigInclude
Definition: TsConfigInclude.php:25
‪$name
‪$name
Definition: phpIntegrityChecker.php:235
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Event\BeforeLoadedPageTsConfigEvent
Definition: BeforeLoadedPageTsConfigEvent.php:28
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Event\ModifyLoadedPageTsConfigEvent
Definition: ModifyLoadedPageTsConfigEvent.php:24
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\RootInclude
Definition: RootInclude.php:27
‪TYPO3\CMS\Core\TypoScript\Tokenizer\TokenizerInterface
Definition: TokenizerInterface.php:40
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder\getPackagePageTsConfigTree
‪getPackagePageTsConfigTree(?PhpFrontend $cache=null)
Definition: TsConfigTreeBuilder.php:155
‪TYPO3\CMS\Core\TypoScript\Tokenizer\TokenizerInterface\tokenize
‪tokenize(string $source)
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Event\BeforeLoadedUserTsConfigEvent
Definition: BeforeLoadedUserTsConfigEvent.php:28
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder
Definition: TsConfigTreeBuilder.php:48
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37