‪TYPO3CMS  ‪main
TypoScriptFrontendController.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\EventDispatcher\EventDispatcherInterface;
19 use Psr\Http\Message\ResponseInterface;
20 use Psr\Http\Message\ServerRequestInterface;
21 use Psr\Log\LoggerAwareInterface;
22 use Psr\Log\LoggerAwareTrait;
23 use Psr\Log\LogLevel;
70 
81 class ‪TypoScriptFrontendController implements LoggerAwareInterface
82 {
83  use LoggerAwareTrait;
84 
91  public int ‪$id;
92 
97  public array ‪$rootLine = [];
98 
105  public ?array ‪$page = [];
106 
114  public int ‪$contentPid = 0;
115 
121 
141  public array ‪$config = [];
142 
146  protected int ‪$cacheTimeOutDefault = 0;
147 
151  protected bool ‪$pageContentWasLoadedFromCache = false;
152 
156  protected int ‪$cacheExpires = 0;
157  private int ‪$cacheGenerated = 0;
158 
164  public string ‪$newHash = '';
165 
179  public array ‪$additionalHeaderData = [];
180 
185  public array ‪$additionalFooterData = [];
186 
192  public string ‪$absRefPrefix = '';
193 
197  public array ‪$register = [];
198 
204  public array ‪$registerStack = [];
205 
212  public array ‪$recordRegister = [];
213 
222  public string ‪$currentRecord = '';
223 
228  protected int ‪$uniqueCounter = 0;
229 
230  protected string ‪$uniqueString = '';
231 
238 
243  public string ‪$content = '';
244 
246 
250  public ?‪ResourceMutex ‪$lock = null;
251 
254  protected array ‪$pageCacheTags = [];
255 
260  protected string ‪$contentType = 'text/html; charset=utf-8';
261 
267 
272  protected ?string ‪$debugInformationHeader = null;
273 
277  public function ‪__construct()
278  {
279  $this->sys_page = GeneralUtility::makeInstance(PageRepository::class);
280  $this->context = GeneralUtility::makeInstance(Context::class);
281  $this->uniqueString = md5(microtime());
282  $cacheManager = GeneralUtility::makeInstance(CacheManager::class);
283  $this->pageCache = $cacheManager->getCache('pages');
284  }
285 
289  public function ‪initializePageRenderer(ServerRequestInterface $request): void
290  {
291  if ($this->pageRenderer !== null) {
292  return;
293  }
294  $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
295  $this->pageRenderer->setTemplateFile('EXT:frontend/Resources/Private/Templates/MainPage.html');
296  // As initPageRenderer could be called in constructor and for USER_INTs, this information is only set
297  // once - in order to not override any previous settings of PageRenderer.
298  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
299  if ($language->hasCustomTypo3Language()) {
300  $locale = GeneralUtility::makeInstance(Locales::class)->createLocale($language->getTypo3Language());
301  } else {
302  $locale = $language->getLocale();
303  }
304  $this->pageRenderer->setLanguage($locale);
305  }
306 
312  public function ‪initializeLanguageService(ServerRequestInterface $request): void
313  {
314  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
315  $this->languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)->createFromSiteLanguage($language);
316  }
317 
321  public function ‪setContentType(string ‪$contentType): void
322  {
323  $this->contentType = ‪$contentType;
324  }
325 
331  {
332  $queryParams = $pageArguments->getDynamicArguments();
333  if (!empty($queryParams) && ($pageArguments->getArguments()['cHash'] ?? false)) {
334  $queryParams['id'] = $pageArguments->‪getPageId();
335  return GeneralUtility::makeInstance(CacheHashCalculator::class)
336  ->getRelevantParameters(‪HttpUtility::buildQueryString($queryParams));
337  }
338  return [];
339  }
340 
358  public function ‪getFromCache(ServerRequestInterface $request): ‪FrontendTypoScript
359  {
360  $pageInformation = $request->getAttribute('frontend.page.information');
361  ‪$rootLine = $pageInformation->getRootLine();
362  $localRootline = $pageInformation->getLocalRootLine();
363  $sysTemplateRows = $pageInformation->getSysTemplateRows();
364  $serializedSysTemplateRows = serialize($sysTemplateRows);
365  $site = $request->getAttribute('site');
366  $isCachingAllowed = $request->getAttribute('frontend.cache.instruction')->isCachingAllowed();
367 
368  $tokenizer = new ‪LossyTokenizer();
369  $treeBuilder = GeneralUtility::makeInstance(SysTemplateTreeBuilder::class);
370  $includeTreeTraverser = new ‪IncludeTreeTraverser();
371  $includeTreeTraverserConditionVerdictAware = new ‪ConditionVerdictAwareIncludeTreeTraverser();
372  $cacheManager = GeneralUtility::makeInstance(CacheManager::class);
374  $typoscriptCache = null;
375  if ($isCachingAllowed) {
376  // disableCache() might have been called by earlier middlewares. This means we don't do fancy cache
377  // stuff, calculate full TypoScript and don't get() from nor set() to typoscript and page cache.
379  $typoscriptCache = $cacheManager->getCache('typoscript');
380  }
381 
382  $topDownRootLine = ‪$rootLine;
383  ksort($topDownRootLine);
384  $expressionMatcherVariables = [
385  'request' => $request,
386  'pageId' => $pageInformation->getId(),
387  // @todo We're using the full page row here to provide all necessary fields (e.g. "backend_layout"),
388  // which are currently not included in the rows, RootlineUtility provides by default. We might
389  // want to switch to $pageInformation->getRootLine() as soon as it contains all fields.
390  'page' => $pageInformation->getPageRecord(),
391  'fullRootLine' => $topDownRootLine,
392  'localRootLine' => $localRootline,
393  'site' => $site,
394  'siteLanguage' => $request->getAttribute('language'),
395  'tsfe' => $this,
396  ];
397 
398  // We *always* need the TypoScript constants, one way or the other: Setup conditions can use constants,
399  // so we need the constants to substitute their values within setup conditions.
400  $constantConditionIncludeListCacheIdentifier = 'constant-condition-include-list-' . sha1($serializedSysTemplateRows);
401  $constantConditionList = [];
402  $constantsAst = new ‪RootNode();
403  $flatConstants = [];
404  $serializedConstantConditionList = '';
405  $gotConstantFromCache = false;
406  if ($isCachingAllowed && $constantConditionIncludeTree = $typoscriptCache->require($constantConditionIncludeListCacheIdentifier)) {
407  // We got the flat list of all constants conditions for this TypoScript combination from cache. Good. We traverse
408  // this list to calculate "current" condition verdicts. With a hash of this list together with a hash of the
409  // TypoScript sys_templates, we try to retrieve the full constants TypoScript from cache.
410  $conditionMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
411  $conditionMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
412  // It does not matter if we use IncludeTreeTraverser or ConditionVerdictAwareIncludeTreeTraverser here:
413  // Condition list is flat, not nested. IncludeTreeTraverser has an if() less, so we use that one.
414  $includeTreeTraverser->traverse($constantConditionIncludeTree, [$conditionMatcherVisitor]);
415  $constantConditionList = $conditionMatcherVisitor->getConditionListWithVerdicts();
416  // Needed for cache identifier calculations. Put into a variable here to not serialize multiple times.
417  $serializedConstantConditionList = serialize($constantConditionList);
418  $constantCacheEntryIdentifier = 'constant-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList);
419  $constantsCacheEntry = $typoscriptCache->require($constantCacheEntryIdentifier);
420  if (is_array($constantsCacheEntry)) {
421  $constantsAst = $constantsCacheEntry['ast'];
422  $flatConstants = $constantsCacheEntry['flatConstants'];
423  $gotConstantFromCache = true;
424  }
425  }
426  if (!$isCachingAllowed || !$gotConstantFromCache) {
427  // We did not get constants from cache, or are not allowed to use cache. We have to build constants from scratch.
428  // This means we'll fetch the full constants include tree (from cache if possible), register the condition
429  // matcher and register the AST builder and traverse include tree to retrieve constants AST and calculate
430  // 'flat constants' from it. Both are cached if allowed afterwards for the 'if' above to kick in next time.
431  $constantIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('constants', $sysTemplateRows, $tokenizer, $site, $typoscriptCache);
432  $conditionMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
433  $conditionMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
434  $includeTreeTraverserConditionVerdictAwareVisitors = [];
435  $includeTreeTraverserConditionVerdictAwareVisitors[] = $conditionMatcherVisitor;
436  $constantAstBuilderVisitor = GeneralUtility::makeInstance(IncludeTreeAstBuilderVisitor::class);
437  $includeTreeTraverserConditionVerdictAwareVisitors[] = $constantAstBuilderVisitor;
438  // We must use ConditionVerdictAwareIncludeTreeTraverser here: This one does not walk into
439  // children for not matching conditions, which is important to create the correct AST.
440  $includeTreeTraverserConditionVerdictAware->traverse($constantIncludeTree, $includeTreeTraverserConditionVerdictAwareVisitors);
441  $constantsAst = $constantAstBuilderVisitor->getAst();
442  // @internal Dispatch an experimental event allowing listeners to still change the constants AST,
443  // to for instance implement nested constants if really needed. Note this event may change
444  // or vanish later without further notice.
445  $constantsAst = GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(new ‪ModifyTypoScriptConstantsEvent($constantsAst))->getConstantsAst();
446  $flatConstants = $constantsAst->flatten();
447  if ($isCachingAllowed) {
448  // We are allowed to cache and can create both the full list of conditions, plus the constant AST and flat constant
449  // list cache entry. To do that, we need all (!) conditions, but the above ConditionVerdictAwareIncludeTreeTraverser
450  // did not find nested conditions if an upper condition did not match. We thus have to traverse include tree a
451  // second time with the IncludeTreeTraverser that does traverse into not matching conditions as well.
452  $includeTreeTraverserVisitors = [];
453  $conditionMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
454  $conditionMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
455  $includeTreeTraverserVisitors[] = $conditionMatcherVisitor;
456  $constantConditionIncludeListAccumulatorVisitor = new ‪IncludeTreeConditionIncludeListAccumulatorVisitor();
457  $includeTreeTraverserVisitors[] = $constantConditionIncludeListAccumulatorVisitor;
458  $includeTreeTraverser->traverse($constantIncludeTree, $includeTreeTraverserVisitors);
459  $constantConditionList = $conditionMatcherVisitor->getConditionListWithVerdicts();
460  // Needed for cache identifier calculations. Put into a variable here to not serialize multiple times.
461  $serializedConstantConditionList = serialize($constantConditionList);
462  $typoscriptCache->set($constantConditionIncludeListCacheIdentifier, 'return unserialize(\'' . addcslashes(serialize($constantConditionIncludeListAccumulatorVisitor->getConditionIncludes()), '\'\\') . '\');');
463  $constantCacheEntryIdentifier = 'constant-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList);
464  $typoscriptCache->set($constantCacheEntryIdentifier, 'return unserialize(\'' . addcslashes(serialize(['ast' => $constantsAst, 'flatConstants' => $flatConstants]), '\'\\') . '\');');
465  }
466  }
467 
468  $frontendTypoScript = new ‪FrontendTypoScript($constantsAst, $flatConstants);
469 
470  // Next step: We have constants and fetch the setup include tree now. We then calculate setup condition verdicts
471  // and set the constants to allow substitution of constants within conditions. Next, we traverse include tree
472  // to calculate conditions verdicts and gather them along the way. A hash of these conditions with their verdicts
473  // is then part of the page cache identifier hash: When a condition on a page creates a different result, the hash
474  // is different from an existing page cache entry and a new one is created later.
475  $setupConditionIncludeListCacheIdentifier = 'setup-condition-include-list-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList);
476  $setupConditionList = [];
477  $gotSetupConditionsFromCache = false;
478  if ($isCachingAllowed && $setupConditionIncludeTree = $typoscriptCache->require($setupConditionIncludeListCacheIdentifier)) {
479  // We got the flat list of all setup conditions for this TypoScript combination from cache. Good. We traverse
480  // this list to calculate "current" condition verdicts, which we need as hash to be part of page cache identifier.
481  $includeTreeTraverserVisitors = [];
482  $setupConditionConstantSubstitutionVisitor = new ‪IncludeTreeSetupConditionConstantSubstitutionVisitor();
483  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flatConstants);
484  $includeTreeTraverserVisitors[] = $setupConditionConstantSubstitutionVisitor;
485  $setupMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
486  $setupMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
487  $includeTreeTraverserVisitors[] = $setupMatcherVisitor;
488  // It does not matter if we use IncludeTreeTraverser or ConditionVerdictAwareIncludeTreeTraverser here:
489  // Condition list is flat, not nested. IncludeTreeTraverser has an if() less, so we use that one.
490  $includeTreeTraverser->traverse($setupConditionIncludeTree, $includeTreeTraverserVisitors);
491  $setupConditionList = $setupMatcherVisitor->getConditionListWithVerdicts();
492  $gotSetupConditionsFromCache = true;
493  }
494  $setupIncludeTree = null;
495  if (!$isCachingAllowed || !$gotSetupConditionsFromCache) {
496  // We did not get setup condition list from cache, or are not allowed to use cache. We have to build setup
497  // condition list from scratch. This means we'll fetch the full setup include tree (from cache if possible),
498  // register the constant substitution visitor, and register condition matcher and register the condition
499  // accumulator visitor.
500  $setupIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('setup', $sysTemplateRows, $tokenizer, $site, $typoscriptCache);
501  $includeTreeTraverserVisitors = [];
502  $setupConditionConstantSubstitutionVisitor = new ‪IncludeTreeSetupConditionConstantSubstitutionVisitor();
503  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flatConstants);
504  $includeTreeTraverserVisitors[] = $setupConditionConstantSubstitutionVisitor;
505  $setupMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
506  $setupMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
507  $includeTreeTraverserVisitors[] = $setupMatcherVisitor;
508  $setupConditionIncludeListAccumulatorVisitor = new ‪IncludeTreeConditionIncludeListAccumulatorVisitor();
509  $includeTreeTraverserVisitors[] = $setupConditionIncludeListAccumulatorVisitor;
510  // It is important we use IncludeTreeTraverser here: We to have the condition verdicts of *all* conditions, plus
511  // want to accumulate all of them. The ConditionVerdictAwareIncludeTreeTraverser wouldn't walk into nested
512  // conditions if an upper one does not match.
513  $includeTreeTraverser->traverse($setupIncludeTree, $includeTreeTraverserVisitors);
514  $setupConditionList = $setupMatcherVisitor->getConditionListWithVerdicts();
515  $typoscriptCache?->set($setupConditionIncludeListCacheIdentifier, 'return unserialize(\'' . addcslashes(serialize($setupConditionIncludeListAccumulatorVisitor->getConditionIncludes()), '\'\\') . '\');');
516  }
517 
518  // We now gathered everything to calculate the page cache identifier: It depends on sys_template rows, the calculated
519  // constant condition verdicts, the setup condition verdicts, plus various not TypoScript related details like
520  // obviously the page id.
521  $this->lock = GeneralUtility::makeInstance(ResourceMutex::class);
522  $this->newHash = $this->‪createHashBase($request, $sysTemplateRows, $constantConditionList, $setupConditionList);
523  if ($isCachingAllowed) {
524  if ($this->‪shouldAcquireCacheData($request)) {
525  // Try to get a page cache row.
526  $pageCacheRow = $this->pageCache->get($this->newHash);
527  if (!is_array($pageCacheRow)) {
528  // Nothing in the cache, we acquire an exclusive lock now.
529  // There are two scenarios when locking: We're either the first process acquiring this lock. This means we'll
530  // "immediately" get it and can continue with page rendering. Or, another process acquired the lock already. In
531  // this case, the below call will wait until the lock is released again. The other process then probably wrote
532  // a page cache entry, which we can use.
533  // To handle the second case - if our process had to wait for another one creating the content for us - we
534  // simply query the page cache again to see if there is a page cache now.
535  $hadToWaitForLock = $this->lock->acquireLock('pages', $this->newHash);
536  // From this point on we're the only one working on that page.
537  if ($hadToWaitForLock) {
538  // Query the cache again to see if the data is there meanwhile: We did not get the lock
539  // immediately, chances are high the other process created a page cache for us.
540  // There is a small chance the other process actually pageCache->set() the content,
541  // but pageCache->get() still returns false, for instance when a database returned "done"
542  // for the INSERT, but SELECT still does not return the new row - may happen in multi-head
543  // DB instances, and with some other distributed cache backends as well. The worst that
544  // can happen here is the page generation is done too often, which we accept as trade-off.
545  $pageCacheRow = $this->pageCache->get($this->newHash);
546  if (is_array($pageCacheRow)) {
547  // We have the content, some other process did the work for us, release our lock again.
548  $this->‪releaseLocks();
549  }
550  }
551  // We keep the lock set, because we are the ones generating the page now and filling the cache.
552  // This indicates that we have to release the lock later in releaseLocks()!
553  }
554  if (is_array($pageCacheRow)) {
555  $this->config['INTincScript'] = $pageCacheRow['INTincScript'];
556  $this->config['INTincScript_ext'] = $pageCacheRow['INTincScript_ext'];
557  $this->config['pageTitleCache'] = $pageCacheRow['pageTitleCache'];
558  $this->content = $pageCacheRow['content'];
559  $this->contentType = $pageCacheRow['contentType'];
560  $this->cacheExpires = $pageCacheRow['expires'];
561  $this->pageCacheTags = $pageCacheRow['cacheTags'];
562  $this->cacheGenerated = $pageCacheRow['tstamp'];
563  $this->pageContentWasLoadedFromCache = true;
564  }
565  } else {
566  // User forced page cache rebuilding. Get a lock for the page content so other processes can't interfere.
567  $this->lock->acquireLock('pages', $this->newHash);
568  }
569  } else {
570  // Caching is not allowed. We'll rebuild the page. Lock this.
571  $this->lock->acquireLock('pages', $this->newHash);
572  }
573 
574  $setupRawConfigAst = null;
575  if (!$isCachingAllowed || !$this->pageContentWasLoadedFromCache || $this->‪isINTincScript()) {
576  // We don't need the full setup AST in many cached scenarios. However, if caching is not allowed, if no page
577  // cache entry could be loaded or if the page cache entry has _INT object, then we still need the full setup AST.
578  // If there is "just" an _INT object, we can use a possible cache entry for the setup AST, which speeds up _INT
579  // parsing quite a bit. In other cases we calculate full setup AST and cache it if allowed.
580  $setupTypoScriptCacheIdentifier = 'setup-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList . serialize($setupConditionList));
581  $gotSetupFromCache = false;
582  if ($isCachingAllowed) {
583  // We need AST, but we are allowed to potentially get it from cache.
584  if ($setupTypoScriptCache = $typoscriptCache->require($setupTypoScriptCacheIdentifier)) {
585  $frontendTypoScript->setSetupTree($setupTypoScriptCache['ast']);
586  $frontendTypoScript->setSetupArray($setupTypoScriptCache['array']);
587  $setupRawConfigAst = $setupTypoScriptCache['ast']->getChildByName('config');
588  $gotSetupFromCache = true;
589  }
590  }
591  if (!$isCachingAllowed || !$gotSetupFromCache) {
592  // We need AST and couldn't get it from cache or are now allowed to. We thus need the full setup
593  // IncludeTree, which we can get from cache again if allowed, or is calculated a-new if not.
594  if (!$isCachingAllowed || $setupIncludeTree === null) {
595  $setupIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('setup', $sysTemplateRows, $tokenizer, $site, $typoscriptCache);
596  }
597  $includeTreeTraverserConditionVerdictAwareVisitors = [];
598  $setupConditionConstantSubstitutionVisitor = new ‪IncludeTreeSetupConditionConstantSubstitutionVisitor();
599  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flatConstants);
600  $includeTreeTraverserConditionVerdictAwareVisitors[] = $setupConditionConstantSubstitutionVisitor;
601  $setupMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
602  $setupMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
603  $includeTreeTraverserConditionVerdictAwareVisitors[] = $setupMatcherVisitor;
604  $setupAstBuilderVisitor = GeneralUtility::makeInstance(IncludeTreeAstBuilderVisitor::class);
605  $setupAstBuilderVisitor->setFlatConstants($flatConstants);
606  $includeTreeTraverserConditionVerdictAwareVisitors[] = $setupAstBuilderVisitor;
607  $includeTreeTraverserConditionVerdictAware->traverse($setupIncludeTree, $includeTreeTraverserConditionVerdictAwareVisitors);
608  $setupAst = $setupAstBuilderVisitor->getAst();
609  // @todo: It would be good to actively remove 'config' from AST and array here
610  // to prevent people from using the unmerged variant. The same
611  // is already done for the determined PAGE 'config' below. This works, but
612  // is currently blocked by functional tests that assert details?
613  $setupRawConfigAst = $setupAst->getChildByName('config');
614  // $setupAst->removeChildByName('config');
615  $frontendTypoScript->setSetupTree($setupAst);
616  $frontendTypoScript->setSetupArray($setupAst->toArray());
617 
618  if ($isCachingAllowed) {
619  // Write cache entry for AST and its array representation, we're allowed to do it.
620  $typoscriptCache->set($setupTypoScriptCacheIdentifier, 'return unserialize(\'' . addcslashes(serialize(['ast' => $setupAst, 'array' => $setupAst->toArray()]), '\'\\') . '\');');
621  }
622  }
623  }
624 
626  $pageArguments = $request->getAttribute('routing');
627  $type = (int)($pageArguments->getPageType() ?: 0);
628 
629  $gotSetupConfigFromCache = false;
630  $setupConfigTypoScriptCacheIdentifier = 'setup-config-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList . serialize($setupConditionList) . $type);
631  if ($isCachingAllowed) {
632  if ($setupConfigTypoScriptCache = $typoscriptCache->require($setupConfigTypoScriptCacheIdentifier)) {
633  $frontendTypoScript->setConfigTree($setupConfigTypoScriptCache['ast']);
634  $frontendTypoScript->setConfigArray($setupConfigTypoScriptCache['array']);
635  $gotSetupConfigFromCache = true;
636  }
637  }
638 
639  if (!$isCachingAllowed || !$this->pageContentWasLoadedFromCache || !$gotSetupConfigFromCache || $this->‪isINTincScript()) {
640  $setupAst = $frontendTypoScript->getSetupTree();
641  $rawSetupPageNodeFromType = null;
642  foreach ($setupAst->getNextChild() as $potentialPageNode) {
643  if ($potentialPageNode->getValue() === 'PAGE') {
644  // @todo: We could potentially remove *all* PAGE objects from setup here. This prevents people
645  // from accessing other ones than the determined one in $frontendTypoScript->getSetupArray().
646  $typeNumChild = $potentialPageNode->getChildByName('typeNum');
647  if ($typeNumChild && $type === (int)$typeNumChild->getValue()) {
648  $rawSetupPageNodeFromType = $potentialPageNode;
649  break;
650  }
651  if (!$typeNumChild && $type === 0) {
652  // The first PAGE node that has no typeNum is considered '0' automatically.
653  $rawSetupPageNodeFromType = $potentialPageNode;
654  break;
655  }
656  }
657  }
658  if (!$rawSetupPageNodeFromType) {
659  $this->logger->error('No page configured for type={type}. There is no TypoScript object of type PAGE with typeNum={type}.', ['type' => $type]);
660  $response = GeneralUtility::makeInstance(ErrorController::class)->internalErrorAction(
661  $request,
662  'No page configured for type=' . $type . '.',
664  );
665  throw new ‪PropagateResponseException($response, 1533931374);
666  }
667  $setupPageAst = new ‪RootNode();
668  foreach ($rawSetupPageNodeFromType->getNextChild() as $child) {
669  $setupPageAst->addChild($child);
670  }
671  if (!$gotSetupConfigFromCache) {
672  $mergedSetupConfigAst = (new ‪SetupConfigMerger())->merge($setupRawConfigAst, $setupPageAst->getChildByName('config'));
673 
674  if ($mergedSetupConfigAst->getChildByName('absRefPrefix') === null) {
675  // Make sure config.absRefPrefix is set, fallback to 'auto'.
676  $absRefPrefixNode = new ‪ChildNode('absRefPrefix');
677  $absRefPrefixNode->setValue('auto');
678  $mergedSetupConfigAst->addChild($absRefPrefixNode);
679  }
680  if ($mergedSetupConfigAst->getChildByName('doctype') === null) {
681  // Make sure config.doctype is set, fallback to 'html5'.
682  $doctypeNode = new ‪ChildNode('doctype');
683  $doctypeNode->setValue('html5');
684  $mergedSetupConfigAst->addChild($doctypeNode);
685  }
686  $mergedSetupConfigAst = GeneralUtility::makeInstance(EventDispatcherInterface::class)
687  ->dispatch(new ‪ModifyTypoScriptConfigEvent($request, $setupAst, $mergedSetupConfigAst))->getConfigTree();
688  $frontendTypoScript->setConfigTree($mergedSetupConfigAst);
689  $setupConfigArray = $mergedSetupConfigAst->toArray();
690  $frontendTypoScript->setConfigArray($setupConfigArray);
691  if ($isCachingAllowed) {
692  $typoscriptCache->set($setupConfigTypoScriptCacheIdentifier, 'return unserialize(\'' . addcslashes(serialize(['ast' => $mergedSetupConfigAst, 'array' => $setupConfigArray]), '\'\\') . '\');');
693  }
694  }
695  if (!$isCachingAllowed || !$this->pageContentWasLoadedFromCache || $this->‪isINTincScript()) {
696  // Remove "page.config" to prevent people from working with the not merged variant.
697  $setupPageAst->removeChildByName('config');
698  $frontendTypoScript->setPageTree($setupPageAst);
699  $frontendTypoScript->setPageArray($setupPageAst->toArray());
700  }
701  }
702 
703  $setupConfigAst = $frontendTypoScript->getConfigTree();
704 
705  if ($this->pageContentWasLoadedFromCache
706  && ($setupConfigAst->getChildByName('debug')?->getValue() || !empty(‪$GLOBALS['TYPO3_CONF_VARS']['FE']['debug']))
707  ) {
708  // Prepare X-TYPO3-Debug-Cache HTTP header
709  $dateFormat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
710  $timeFormat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
711  $this->debugInformationHeader = 'Cached page generated ' . date($dateFormat . ' ' . $timeFormat, $this->cacheGenerated)
712  . '. Expires ' . date($dateFormat . ' ' . $timeFormat, $this->cacheExpires);
713  }
714 
715  if ($setupConfigAst->getChildByName('no_cache')?->getValue()) {
716  // Disable cache if config.no_cache is set!
717  $cacheInstruction = $request->getAttribute('frontend.cache.instruction');
718  $cacheInstruction->disableCache('EXT:frontend: Disabled cache due to TypoScript "config.no_cache = 1"');
719  }
720 
721  return $frontendTypoScript;
722  }
723 
731  protected function ‪shouldAcquireCacheData(ServerRequestInterface $request): bool
732  {
733  // Trigger event for possible by-pass of requiring of page cache.
734  $event = new ‪ShouldUseCachedPageDataIfAvailableEvent($request, $this, $request->getAttribute('frontend.cache.instruction')->isCachingAllowed());
735  GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch($event);
736  return $event->shouldUseCachedPageData();
737  }
738 
750  protected function ‪createHashBase(ServerRequestInterface $request, array $sysTemplateRows, array $constantConditionList, array $setupConditionList): string
751  {
752  $pageInformation = $request->getAttribute('frontend.page.information');
753  $pageId = $pageInformation->getId();
754  $site = $request->getAttribute('site');
755  $pageArguments = $request->getAttribute('routing');
756  $pageCacheIdentifierParameters = [
757  'id' => $pageId,
758  'type' => (int)($pageArguments->getPageType() ?: 0),
759  'groupIds' => implode(',', $this->context->getAspect('frontend.user')->getGroupIds()),
760  'MP' => $pageInformation->getMountPoint(),
761  'site' => $site->getIdentifier(),
762  // Ensure the language base is used for the hash base calculation as well, otherwise TypoScript and page-related rendering
763  // is not cached properly as we don't have any language-specific conditions anymore
764  'siteBase' => (string)$request->getAttribute('language', $site->getDefaultLanguage())->getBase(),
765  // additional variation trigger for static routes
766  'staticRouteArguments' => $pageArguments->getStaticArguments(),
767  // dynamic route arguments (if route was resolved)
768  'dynamicArguments' => $this->‪getRelevantParametersForCachingFromPageArguments($pageArguments),
769  'sysTemplateRows' => $sysTemplateRows,
770  'constantConditionList' => $constantConditionList,
771  'setupConditionList' => $setupConditionList,
772  ];
773  $pageCacheIdentifierParameters = GeneralUtility::makeInstance(EventDispatcherInterface::class)
774  ->dispatch(new ‪BeforePageCacheIdentifierIsHashedEvent($request, $pageCacheIdentifierParameters))
775  ->getPageCacheIdentifierParameters();
776  return $pageId . '_' . sha1(serialize($pageCacheIdentifierParameters));
777  }
778 
784  public function ‪isGeneratePage(): bool
785  {
787  }
788 
796  protected function ‪setPageCacheContent(
797  ServerRequestInterface $request,
798  string ‪$content,
799  array $INTincScript,
800  array $INTincScript_ext,
801  array $pageTitleCache,
802  int $expirationTstamp
803  ): array {
804  $pageInformation = $request->getAttribute('frontend.page.information');
805  $pageId = $pageInformation->getId();
806  $pageRecord = $pageInformation->getPageRecord();
807  $cacheData = [
808  'page_id' => $pageId,
809  'content' => ‪$content,
810  'contentType' => ‪$this->contentType,
811  'INTincScript' => $INTincScript,
812  'INTincScript_ext' => $INTincScript_ext,
813  'pageTitleCache' => $pageTitleCache,
814  'expires' => $expirationTstamp,
815  'tstamp' => ‪$GLOBALS['EXEC_TIME'],
816  ];
817  $this->cacheExpires = $expirationTstamp;
818  $this->pageCacheTags[] = 'pageId_' . $pageId;
819  // Respect the page cache when content of pid is shown
820  if ($pageId !== $pageInformation->getContentFromPid()) {
821  $this->pageCacheTags[] = 'pageId_' . $pageInformation->getContentFromPid();
822  }
823  if (!empty($pageRecord['cache_tags'])) {
824  $tags = ‪GeneralUtility::trimExplode(',', $pageRecord['cache_tags'], true);
825  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
826  }
827  $this->pageCacheTags = array_unique($this->pageCacheTags);
828  // Add the cache themselves as well, because they are fetched by getPageCacheTags()
829  $cacheData['cacheTags'] = ‪$this->pageCacheTags;
830  $this->pageCache->set($this->newHash, $cacheData, $this->pageCacheTags, $expirationTstamp - ‪$GLOBALS['EXEC_TIME']);
831  return $cacheData;
832  }
833 
844  protected function ‪setSysLastChanged(ServerRequestInterface $request): void
845  {
846  // Only update if browsing the live workspace
847  $isInWorkspace = $this->context->getPropertyFromAspect('workspace', 'isOffline', false);
848  if ($isInWorkspace) {
849  return;
850  }
851  $pageInformation = $request->getAttribute('frontend.page.information');
852  $pageRecord = $pageInformation->getPageRecord();
853  if ($pageRecord['SYS_LASTCHANGED'] < (int)($this->register['SYS_LASTCHANGED'] ?? 0)) {
854  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
855  $pageId = $pageRecord['_LOCALIZED_UID'] ?? $pageInformation->getId();
856  $connection->update(
857  'pages',
858  [
859  'SYS_LASTCHANGED' => (int)$this->register['SYS_LASTCHANGED'],
860  ],
861  [
862  'uid' => (int)$pageId,
863  ]
864  );
865  }
866  }
867 
872  public function ‪addCacheTags(array $tags): void
873  {
874  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
875  }
876 
877  public function ‪getPageCacheTags(): array
878  {
880  }
881 
887  public function ‪preparePageContentGeneration(ServerRequestInterface $request): void
888  {
889  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
890  // calculate the absolute path prefix
891  if (!empty($this->absRefPrefix = trim($typoScriptConfigArray['absRefPrefix']))) {
892  if ($this->absRefPrefix === 'auto') {
893  $normalizedParams = $request->getAttribute('normalizedParams');
894  $this->absRefPrefix = $normalizedParams->getSitePath();
895  }
896  }
897  // config.forceAbsoluteUrls will override absRefPrefix
898  if ($typoScriptConfigArray['forceAbsoluteUrls'] ?? false) {
899  $normalizedParams = $request->getAttribute('normalizedParams');
900  $this->absRefPrefix = $normalizedParams->getSiteUrl();
901  }
902 
903  $docType = DocType::createFromConfigurationKey($typoScriptConfigArray['doctype']);
904  $this->pageRenderer->setDocType($docType);
905 
906  // Global content object
907  $this->‪newCObj($request);
908  }
909 
916  public function ‪generatePage_postProcessing(ServerRequestInterface $request): void
917  {
918  $this->‪setAbsRefPrefix();
919  $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
920  $usePageCache = $request->getAttribute('frontend.cache.instruction')->isCachingAllowed();
921  $event = new ‪AfterCacheableContentIsGeneratedEvent($request, $this, $this->newHash, $usePageCache);
922  $event = $eventDispatcher->dispatch($event);
923 
924  // Processing if caching is enabled
925  if ($event->isCachingEnabled()) {
926  // Seconds until a cached page is too old
927  $cacheTimeout = $this->‪get_cache_timeout($request);
928  $timeOutTime = ‪$GLOBALS['EXEC_TIME'] + $cacheTimeout;
929  // Write the page to cache, but do not cache localRootLine since that is always determined
930  // and coming from PageInformation->getLocalRootLine().
931  $cachedInformation = $this->‪setPageCacheContent(
932  $request,
933  $this->content,
934  $this->config['INTincScript'] ?? [],
935  $this->config['INTincScript_ext'] ?? [],
936  $this->config['pageTitleCache'] ?? [],
937  $timeOutTime
938  );
939  // Event for cache post processing (eg. writing static files)
940  $event = new ‪AfterCachedPageIsPersistedEvent($request, $this, $this->newHash, $cachedInformation, $cacheTimeout);
941  $eventDispatcher->dispatch($event);
942  }
943  $this->‪setSysLastChanged($request);
944  }
945 
952  public function ‪generatePageTitle(ServerRequestInterface $request): string
953  {
954  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
955  // config.noPageTitle = 2 - means do not render the page title
956  if ((int)($typoScriptConfigArray['noPageTitle'] ?? 0) === 2) {
957  return '';
958  }
959 
960  // Check for a custom pageTitleSeparator, and perform stdWrap on it
961  $pageTitleSeparator = (string)$this->cObj->stdWrapValue('pageTitleSeparator', $typoScriptConfigArray);
962  if ($pageTitleSeparator !== '' && $pageTitleSeparator === ($typoScriptConfigArray['pageTitleSeparator'] ?? '')) {
963  $pageTitleSeparator .= ' ';
964  }
965 
966  $titleProvider = GeneralUtility::makeInstance(PageTitleProviderManager::class);
967  if (!empty($this->config['pageTitleCache'])) {
968  $titleProvider->setPageTitleCache($this->config['pageTitleCache']);
969  }
970  $pageTitle = $titleProvider->getTitle($request);
971  $this->config['pageTitleCache'] = $titleProvider->getPageTitleCache();
972 
973  $titleTagContent = $this->‪printTitle(
974  $request,
975  $pageTitle,
976  (bool)($typoScriptConfigArray['noPageTitle'] ?? false),
977  (bool)($typoScriptConfigArray['pageTitleFirst'] ?? false),
978  $pageTitleSeparator,
979  (bool)($typoScriptConfigArray['showWebsiteTitle'] ?? true)
980  );
981 
982  if (isset($typoScriptConfigArray['pageTitle.']) && is_array($typoScriptConfigArray['pageTitle.'])) {
983  // stdWrap for pageTitle if set in config.pageTitle.
984  $pageTitleStdWrapArray = [
985  'pageTitle' => $titleTagContent,
986  'pageTitle.' => $typoScriptConfigArray['pageTitle.'],
987  ];
988  $titleTagContent = $this->cObj->stdWrapValue('pageTitle', $pageTitleStdWrapArray);
989  }
990 
991  if ($titleTagContent !== '') {
992  $this->pageRenderer->setTitle($titleTagContent);
993  }
994  return (string)$titleTagContent;
995  }
996 
1008  protected function ‪printTitle(ServerRequestInterface $request, string $pageTitle, bool $noPageTitle = false, bool $showPageTitleFirst = false, string $pageTitleSeparator = '', bool $showWebsiteTitle = true): string
1009  {
1010  $websiteTitle = $showWebsiteTitle ? $this->‪getWebsiteTitle($request) : '';
1011  $pageTitle = $noPageTitle ? '' : $pageTitle;
1012  // only show a separator if there are both site title and page title
1013  if ($pageTitle === '' || $websiteTitle === '') {
1014  $pageTitleSeparator = '';
1015  } elseif (empty($pageTitleSeparator)) {
1016  // use the default separator if non given
1017  $pageTitleSeparator = ': ';
1018  }
1019  if ($showPageTitleFirst) {
1020  return $pageTitle . $pageTitleSeparator . $websiteTitle;
1021  }
1022  return $websiteTitle . $pageTitleSeparator . $pageTitle;
1023  }
1024 
1025  protected function ‪getWebsiteTitle(ServerRequestInterface $request): string
1026  {
1027  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
1028  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
1029  if (trim($language->getWebsiteTitle()) !== '') {
1030  return trim($language->getWebsiteTitle());
1031  }
1032  $siteConfiguration = $request->getAttribute('site')->getConfiguration();
1033  if (trim($siteConfiguration['websiteTitle'] ?? '') !== '') {
1034  return trim($siteConfiguration['websiteTitle']);
1035  }
1036  return '';
1037  }
1038 
1044  public function ‪INTincScript(ServerRequestInterface $request): void
1045  {
1046  $this->additionalHeaderData = $this->config['INTincScript_ext']['additionalHeaderData'] ?? [];
1047  $this->additionalFooterData = $this->config['INTincScript_ext']['additionalFooterData'] ?? [];
1048  if (empty($this->config['INTincScript_ext']['pageRendererState'])) {
1049  $this->‪initializePageRenderer($request);
1050  } else {
1051  $pageRendererState = unserialize($this->config['INTincScript_ext']['pageRendererState'], ['allowed_classes' => [Locale::class]]);
1052  $this->pageRenderer->updateState($pageRendererState);
1053  }
1054  if (!empty($this->config['INTincScript_ext']['assetCollectorState'])) {
1055  $assetCollectorState = unserialize($this->config['INTincScript_ext']['assetCollectorState'], ['allowed_classes' => false]);
1056  GeneralUtility::makeInstance(AssetCollector::class)->updateState($assetCollectorState);
1057  }
1058 
1060  $this->‪getTimeTracker()->push('Substitute header section');
1061  $this->‪INTincScript_loadJSCode();
1062  $this->‪generatePageTitle($request);
1063 
1064  $this->content = str_replace(
1065  [
1066  '<!--HD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
1067  '<!--FD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
1068  ],
1069  [
1070  implode(LF, $this->additionalHeaderData),
1071  implode(LF, $this->additionalFooterData),
1072  ],
1073  $this->pageRenderer->renderJavaScriptAndCssForProcessingOfUncachedContentObjects($this->content, $this->config['INTincScript_ext']['divKey'])
1074  );
1075  // Replace again, because header and footer data and page renderer replacements may introduce additional placeholders (see #44825)
1077  $this->‪setAbsRefPrefix();
1078  $this->‪getTimeTracker()->pull();
1079  }
1080 
1086  protected function ‪recursivelyReplaceIntPlaceholdersInContent(ServerRequestInterface $request): void
1087  {
1088  do {
1089  $nonCacheableData = $this->config['INTincScript'];
1090  $this->‪processNonCacheableContentPartsAndSubstituteContentMarkers($nonCacheableData, $request);
1091  // Check if there were new items added to INTincScript during the previous execution:
1092  // array_diff_assoc throws notices if values are arrays but not strings. We suppress this here.
1093  $nonCacheableData = @array_diff_assoc($this->config['INTincScript'], $nonCacheableData);
1094  $reprocess = count($nonCacheableData) > 0;
1095  } while ($reprocess);
1096  }
1097 
1107  protected function ‪processNonCacheableContentPartsAndSubstituteContentMarkers(array $nonCacheableData, ServerRequestInterface $request): void
1108  {
1109  $timeTracker = $this->‪getTimeTracker();
1110  $timeTracker->push('Split content');
1111  // Splits content with the key.
1112  $contentSplitByUncacheableMarkers = explode('<!--INT_SCRIPT.', $this->content);
1113  $this->content = '';
1114  $timeTracker->setTSlogMessage('Parts: ' . count($contentSplitByUncacheableMarkers), LogLevel::INFO);
1115  $timeTracker->pull();
1116  foreach ($contentSplitByUncacheableMarkers as $counter => $contentPart) {
1117  // If the split had a comment-end after 32 characters it's probably a split-string
1118  if (substr($contentPart, 32, 3) === '-->') {
1119  $nonCacheableKey = 'INT_SCRIPT.' . substr($contentPart, 0, 32);
1120  if (is_array($nonCacheableData[$nonCacheableKey] ?? false)) {
1121  $label = 'Include ' . $nonCacheableData[$nonCacheableKey]['type'];
1122  $timeTracker->push($label);
1123  $nonCacheableContent = '';
1124  $contentObjectRendererForNonCacheable = unserialize($nonCacheableData[$nonCacheableKey]['cObj']);
1125  if ($contentObjectRendererForNonCacheable instanceof ‪ContentObjectRenderer) {
1126  $contentObjectRendererForNonCacheable->setRequest($request);
1127  $nonCacheableContent = match ($nonCacheableData[$nonCacheableKey]['type']) {
1128  'COA' => $contentObjectRendererForNonCacheable->cObjGetSingle('COA', $nonCacheableData[$nonCacheableKey]['conf']),
1129  'FUNC' => $contentObjectRendererForNonCacheable->cObjGetSingle('USER', $nonCacheableData[$nonCacheableKey]['conf']),
1130  'POSTUSERFUNC' => $contentObjectRendererForNonCacheable->callUserFunction($nonCacheableData[$nonCacheableKey]['postUserFunc'], $nonCacheableData[$nonCacheableKey]['conf'], $nonCacheableData[$nonCacheableKey]['content']),
1131  default => '',
1132  };
1133  }
1134  $this->content .= $nonCacheableContent;
1135  $this->content .= substr($contentPart, 35);
1136  $timeTracker->pull($nonCacheableContent);
1137  } else {
1138  $this->content .= substr($contentPart, 35);
1139  }
1140  } elseif ($counter) {
1141  // If it's not the first entry (which would be "0" of the array keys), then re-add the INT_SCRIPT part
1142  $this->content .= '<!--INT_SCRIPT.' . $contentPart;
1143  } else {
1144  $this->content .= $contentPart;
1145  }
1146  }
1147  // invokes permanent, general handlers
1148  foreach ($nonCacheableData as $item) {
1149  if (empty($item['permanent']) || empty($item['target'])) {
1150  continue;
1151  }
1152  $parameters = array_merge($item['parameters'] ?? [], ['content' => $this->content]);
1153  $this->content = GeneralUtility::callUserFunction($item['target'], $parameters) ?? ‪$this->content;
1154  }
1155  }
1156 
1163  public function ‪INTincScript_loadJSCode(): void
1164  {
1165  // Prepare code and placeholders for additional header and footer files (and make sure that this isn't called twice)
1166  if ($this->‪isINTincScript() && (!isset($this->config['INTincScript_ext']) || $this->config['INTincScript_ext'] === [])) {
1167  $substituteHash = $this->‪uniqueHash();
1168  $this->config['INTincScript_ext']['divKey'] = $substituteHash;
1169  // Storing the header-data array
1170  $this->config['INTincScript_ext']['additionalHeaderData'] = ‪$this->additionalHeaderData;
1171  // Storing the footer-data array
1172  $this->config['INTincScript_ext']['additionalFooterData'] = ‪$this->additionalFooterData;
1173  // Clearing the array
1174  $this->additionalHeaderData = ['<!--HD_' . $substituteHash . '-->'];
1175  // Clearing the array
1176  $this->additionalFooterData = ['<!--FD_' . $substituteHash . '-->'];
1177  }
1178  }
1179 
1186  public function ‪isINTincScript(): bool
1187  {
1188  return !empty($this->config['INTincScript']) && is_array($this->config['INTincScript']);
1189  }
1190 
1196  public function ‪applyHttpHeadersToResponse(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
1197  {
1198  $response = $response->withHeader('Content-Type', $this->contentType);
1199  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
1200  if (empty($typoScriptConfigArray['disableLanguageHeader'])) {
1201  // Set header for content language unless disabled
1202  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
1203  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
1204  $response = $response->withHeader('Content-Language', (string)$language->getLocale());
1205  }
1206 
1207  // Add a Response header to show debug information if a page was fetched from cache
1208  if ($this->debugInformationHeader) {
1209  $response = $response->withHeader('X-TYPO3-Debug-Cache', $this->debugInformationHeader);
1210  }
1211 
1212  // Set cache related headers to client (used to enable proxy / client caching!)
1213  $headers = $this->‪getCacheHeaders($request);
1214  foreach ($headers as $header => $value) {
1215  $response = $response->withHeader($header, $value);
1216  }
1217  // Set additional headers if any have been configured via TypoScript
1218  $additionalHeaders = $this->‪getAdditionalHeaders($request);
1219  foreach ($additionalHeaders as $headerConfig) {
1220  [$header, $value] = ‪GeneralUtility::trimExplode(':', $headerConfig['header'], false, 2);
1221  if ($headerConfig['statusCode']) {
1222  $response = $response->withStatus((int)$headerConfig['statusCode']);
1223  }
1224  if ($headerConfig['replace']) {
1225  $response = $response->withHeader($header, $value);
1226  } else {
1227  $response = $response->withAddedHeader($header, $value);
1228  }
1229  }
1230  return $response;
1231  }
1232 
1236  protected function ‪getCacheHeaders(ServerRequestInterface $request): array
1237  {
1238  $headers = [];
1239  // Getting status whether we can send cache control headers for proxy caching:
1240  $doCache = $this->‪isStaticCacheble($request);
1241  $isBackendUserLoggedIn = $this->context->getPropertyFromAspect('backend.user', 'isLoggedIn', false);
1242  $isInWorkspace = $this->context->getPropertyFromAspect('workspace', 'isOffline', false);
1243  // Finally, when backend users are logged in, do not send cache headers at all (Admin Panel might be displayed for instance).
1244  $isClientCachable = $doCache && !$isBackendUserLoggedIn && !$isInWorkspace;
1245  if ($isClientCachable) {
1246  // Only send the headers to the client that they are allowed to cache if explicitly activated.
1247  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
1248  if (!empty($typoScriptConfigArray['sendCacheHeaders'])) {
1249  $headers = [
1250  'Expires' => gmdate('D, d M Y H:i:s T', $this->cacheExpires),
1251  'ETag' => '"' . md5($this->content) . '"',
1252  'Cache-Control' => 'max-age=' . ($this->cacheExpires - ‪$GLOBALS['EXEC_TIME']),
1253  // no-cache
1254  'Pragma' => 'public',
1255  ];
1256  }
1257  } else {
1258  // "no-store" is used to ensure that the client HAS to ask the server every time, and is not allowed to store anything at all
1259  $headers = [
1260  'Cache-Control' => 'private, no-store',
1261  ];
1262  // Now, if a backend user is logged in, tell him in the Admin Panel log what the caching status would have been:
1263  if ($isBackendUserLoggedIn) {
1264  if ($doCache) {
1265  $this->‪getTimeTracker()->setTSlogMessage('Cache-headers with max-age "' . ($this->cacheExpires - ‪$GLOBALS['EXEC_TIME']) . '" would have been sent');
1266  } else {
1267  $reasonMsg = [];
1268  if (!$request->getAttribute('frontend.cache.instruction')->isCachingAllowed()) {
1269  $reasonMsg[] = 'Caching disabled.';
1270  }
1271  if ($this->‪isINTincScript()) {
1272  $reasonMsg[] = '*_INT object(s) on page.';
1273  }
1274  if ($this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn', false)) {
1275  $reasonMsg[] = 'Frontend user logged in.';
1276  }
1277  $this->‪getTimeTracker()->setTSlogMessage('Cache-headers would disable proxy caching! Reason(s): "' . implode(' ', $reasonMsg) . '"', LogLevel::NOTICE);
1278  }
1279  }
1280  }
1281  return $headers;
1282  }
1283 
1298  public function ‪isStaticCacheble(ServerRequestInterface $request): bool
1299  {
1300  $isCachingAllowed = $request->getAttribute('frontend.cache.instruction')->isCachingAllowed();
1301  return $isCachingAllowed && !$this->‪isINTincScript() && !$this->context->getAspect('frontend.user')->isUserOrGroupSet();
1302  }
1303 
1310  public function ‪newCObj(ServerRequestInterface $request): void
1311  {
1312  $this->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $this);
1313  $this->cObj->setRequest($request);
1314  $this->cObj->start($request->getAttribute('frontend.page.information')->getPageRecord(), 'pages');
1315  }
1316 
1323  protected function ‪setAbsRefPrefix(): void
1324  {
1325  if (!$this->absRefPrefix) {
1326  return;
1327  }
1328  $encodedAbsRefPrefix = htmlspecialchars($this->absRefPrefix, ENT_QUOTES | ENT_HTML5);
1329  $search = [
1330  '"_assets/',
1331  '"typo3temp/',
1334  ];
1335  $replace = [
1336  '"' . $encodedAbsRefPrefix . '_assets/',
1337  '"' . $encodedAbsRefPrefix . 'typo3temp/',
1338  '"' . $encodedAbsRefPrefix . ‪PathUtility::stripPathSitePrefix(‪Environment::getExtensionsPath()) . '/',
1339  '"' . $encodedAbsRefPrefix . ‪PathUtility::stripPathSitePrefix(‪Environment::getFrameworkBasePath()) . '/',
1340  ];
1341  // Process additional directories
1342  $directories = ‪GeneralUtility::trimExplode(',', ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalAbsRefPrefixDirectories'], true);
1343  foreach ($directories as $directory) {
1344  $search[] = '"' . $directory;
1345  $replace[] = '"' . $encodedAbsRefPrefix . $directory;
1346  }
1347  $this->content = str_replace(
1348  $search,
1349  $replace,
1350  $this->content
1351  );
1352  }
1353 
1363  public function ‪logDeprecatedTyposcript(string $typoScriptProperty, string $explanation = ''): void
1364  {
1365  $explanationText = $explanation !== '' ? ' - ' . $explanation : '';
1366  $this->‪getTimeTracker()->setTSlogMessage($typoScriptProperty . ' is deprecated.' . $explanationText, LogLevel::WARNING);
1367  trigger_error('TypoScript property ' . $typoScriptProperty . ' is deprecated' . $explanationText, E_USER_DEPRECATED);
1368  }
1369 
1378  public function ‪uniqueHash(string $str = ''): string
1379  {
1380  return md5($this->uniqueString . '_' . $str . $this->uniqueCounter++);
1381  }
1382 
1389  public function ‪set_no_cache(string $reason = ''): void
1390  {
1391  $warning = '';
1392  ‪$context = [];
1393  if ($reason !== '') {
1394  $warning = '$TSFE->set_no_cache() was triggered. Reason: {reason}.';
1395  ‪$context['reason'] = $reason;
1396  } else {
1397  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
1398  if (isset($trace[0]['class'])) {
1399  ‪$context['class'] = $trace[0]['class'];
1400  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {class} on line {line}.';
1401  }
1402  if (isset($trace[0]['function'])) {
1403  ‪$context['function'] = $trace[0]['function'];
1404  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {class}->{function} on line {line}.';
1405  }
1406  if (‪$context === []) {
1407  // Only store the filename, not the full path for safety reasons
1408  ‪$context['file'] = basename($trace[0]['file']);
1409  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {file} on line {line}.';
1410  }
1411  ‪$context['line'] = $trace[0]['line'];
1412  }
1413  if (‪$GLOBALS['TYPO3_CONF_VARS']['FE']['disableNoCacheParameter']) {
1414  $warning .= ' However, $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set, so it will be ignored!';
1415  $this->‪getTimeTracker()->setTSlogMessage($warning, LogLevel::NOTICE);
1416  } else {
1417  $warning .= ' Caching is disabled!';
1419  $request = ‪$GLOBALS['TYPO3_REQUEST'];
1420  $cacheInstruction = $request->getAttribute('frontend.cache.instruction');
1421  $cacheInstruction->disableCache('EXT:frontend: Caching disabled using deprecated set_no_cache().');
1422  }
1423  $this->logger->notice($warning, ‪$context);
1424  }
1425 
1432  public function ‪set_cache_timeout_default(int $seconds): void
1433  {
1434  $seconds = (int)$seconds;
1435  if ($seconds > 0) {
1436  $this->cacheTimeOutDefault = $seconds;
1437  }
1438  }
1439 
1443  protected function ‪get_cache_timeout(ServerRequestInterface $request): int
1444  {
1445  $pageInformation = $request->getAttribute('frontend.page.information');
1446  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
1447  return GeneralUtility::makeInstance(CacheLifetimeCalculator::class)
1448  ->calculateLifetimeForPage(
1449  $pageInformation->getId(),
1450  $pageInformation->getPageRecord(),
1451  $typoScriptConfigArray,
1452  $this->cacheTimeOutDefault,
1453  $this->context
1454  );
1455  }
1456 
1464  public function ‪sL(string $input): string
1465  {
1466  return $this->languageService->sL($input);
1467  }
1468 
1476  public function ‪releaseLocks(): void
1477  {
1478  $this->lock?->releaseLock('pages');
1479  }
1480 
1484  protected function ‪getAdditionalHeaders(ServerRequestInterface $request): array
1485  {
1486  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
1487  if (!isset($typoScriptConfigArray['additionalHeaders.'])) {
1488  return [];
1489  }
1490  $additionalHeaders = [];
1491  $additionalHeadersConfig = $typoScriptConfigArray['additionalHeaders.'];
1492  ksort($additionalHeadersConfig);
1493  foreach ($additionalHeadersConfig as $options) {
1494  if (!is_array($options)) {
1495  continue;
1496  }
1497  $header = trim($options['header'] ?? '');
1498  if ($header === '') {
1499  continue;
1500  }
1501  $additionalHeaders[] = [
1502  'header' => $header,
1503  // "replace existing headers" is turned on by default, unless turned off
1504  'replace' => ($options['replace'] ?? '') !== '0',
1505  'statusCode' => (int)($options['httpResponseCode'] ?? 0) ?: null,
1506  ];
1507  }
1508  return $additionalHeaders;
1509  }
1510 
1512  {
1513  return ‪$GLOBALS['BE_USER'] ?? null;
1514  }
1515 
1516  protected function ‪getTimeTracker(): ‪TimeTracker
1517  {
1518  return GeneralUtility::makeInstance(TimeTracker::class);
1519  }
1520 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setAbsRefPrefix
‪setAbsRefPrefix()
Definition: TypoScriptFrontendController.php:1323
‪TYPO3\CMS\Core\Routing\PageArguments
Definition: PageArguments.php:26
‪TYPO3\CMS\Core\TypoScript\AST\Merger\SetupConfigMerger
Definition: SetupConfigMerger.php:35
‪TYPO3\CMS\Core\Page\AssetCollector
Definition: AssetCollector.php:42
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static stripPathSitePrefix(string $path)
Definition: PathUtility.php:428
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\uniqueHash
‪string uniqueHash(string $str='')
Definition: TypoScriptFrontendController.php:1378
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSetupConditionConstantSubstitutionVisitor
Definition: IncludeTreeSetupConditionConstantSubstitutionVisitor.php:36
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$contentType
‪string $contentType
Definition: TypoScriptFrontendController.php:260
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$uniqueCounter
‪int $uniqueCounter
Definition: TypoScriptFrontendController.php:228
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$currentRecord
‪string $currentRecord
Definition: TypoScriptFrontendController.php:222
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\generatePageTitle
‪generatePageTitle(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:952
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\recursivelyReplaceIntPlaceholdersInContent
‪recursivelyReplaceIntPlaceholdersInContent(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1086
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cacheExpires
‪int $cacheExpires
Definition: TypoScriptFrontendController.php:156
‪TYPO3\CMS\Core\Locking\ResourceMutex
Definition: ResourceMutex.php:54
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initializeLanguageService
‪initializeLanguageService(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:312
‪TYPO3\CMS\Backend\FrontendBackendUserAuthentication
Definition: FrontendBackendUserAuthentication.php:29
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Frontend\Event\ModifyTypoScriptConfigEvent
Definition: ModifyTypoScriptConfigEvent.php:43
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isGeneratePage
‪isGeneratePage()
Definition: TypoScriptFrontendController.php:784
‪TYPO3\CMS\Core\Routing\PageArguments\getPageId
‪getPageId()
Definition: PageArguments.php:95
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$sys_page
‪PageRepository $sys_page
Definition: TypoScriptFrontendController.php:120
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isStaticCacheble
‪isStaticCacheble(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1298
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$register
‪array $register
Definition: TypoScriptFrontendController.php:197
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\IncludeTreeTraverser
Definition: IncludeTreeTraverser.php:30
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\printTitle
‪string printTitle(ServerRequestInterface $request, string $pageTitle, bool $noPageTitle=false, bool $showPageTitleFirst=false, string $pageTitleSeparator='', bool $showWebsiteTitle=true)
Definition: TypoScriptFrontendController.php:1008
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setPageCacheContent
‪setPageCacheContent(ServerRequestInterface $request, string $content, array $INTincScript, array $INTincScript_ext, array $pageTitleCache, int $expirationTstamp)
Definition: TypoScriptFrontendController.php:796
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$additionalFooterData
‪array $additionalFooterData
Definition: TypoScriptFrontendController.php:185
‪TYPO3\CMS\Core\Core\Environment\getExtensionsPath
‪static getExtensionsPath()
Definition: Environment.php:253
‪TYPO3\CMS\Core\Localization\Locales
Definition: Locales.php:36
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setContentType
‪setContentType(string $contentType)
Definition: TypoScriptFrontendController.php:321
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeAstBuilderVisitor
Definition: IncludeTreeAstBuilderVisitor.php:39
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageContentWasLoadedFromCache
‪bool $pageContentWasLoadedFromCache
Definition: TypoScriptFrontendController.php:151
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$page
‪array $page
Definition: TypoScriptFrontendController.php:105
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$registerStack
‪array $registerStack
Definition: TypoScriptFrontendController.php:204
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\logDeprecatedTyposcript
‪logDeprecatedTyposcript(string $typoScriptProperty, string $explanation='')
Definition: TypoScriptFrontendController.php:1363
‪TYPO3\CMS\Frontend\Event\BeforePageCacheIdentifierIsHashedEvent
Definition: BeforePageCacheIdentifierIsHashedEvent.php:37
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$additionalHeaderData
‪array $additionalHeaderData
Definition: TypoScriptFrontendController.php:179
‪TYPO3\CMS\Core\Error\Http\StatusException
Definition: StatusException.php:26
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\generatePage_postProcessing
‪generatePage_postProcessing(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:916
‪TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent
Definition: AfterCacheableContentIsGeneratedEvent.php:29
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:44
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isINTincScript
‪bool isINTincScript()
Definition: TypoScriptFrontendController.php:1186
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\processNonCacheableContentPartsAndSubstituteContentMarkers
‪processNonCacheableContentPartsAndSubstituteContentMarkers(array $nonCacheableData, ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1107
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeConditionIncludeListAccumulatorVisitor
Definition: IncludeTreeConditionIncludeListAccumulatorVisitor.php:32
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\get_cache_timeout
‪get_cache_timeout(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1443
‪TYPO3\CMS\Frontend\Cache\CacheLifetimeCalculator
Definition: CacheLifetimeCalculator.php:39
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initializePageRenderer
‪initializePageRenderer(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:289
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageRenderer
‪PageRenderer $pageRenderer
Definition: TypoScriptFrontendController.php:252
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageCache
‪FrontendInterface $pageCache
Definition: TypoScriptFrontendController.php:253
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getCacheHeaders
‪getCacheHeaders(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1236
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\applyHttpHeadersToResponse
‪applyHttpHeadersToResponse(ServerRequestInterface $request, ResponseInterface $response)
Definition: TypoScriptFrontendController.php:1196
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getPageCacheTags
‪getPageCacheTags()
Definition: TypoScriptFrontendController.php:877
‪TYPO3\CMS\Core\Utility\HttpUtility\buildQueryString
‪static string buildQueryString(array $parameters, string $prependCharacter='', bool $skipEmptyParameters=false)
Definition: HttpUtility.php:124
‪TYPO3\CMS\Core\PageTitle\PageTitleProviderManager
Definition: PageTitleProviderManager.php:33
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\RENDERING_INSTRUCTIONS_NOT_CONFIGURED
‪const RENDERING_INSTRUCTIONS_NOT_CONFIGURED
Definition: PageAccessFailureReasons.php:34
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getBackendUser
‪getBackendUser()
Definition: TypoScriptFrontendController.php:1511
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$contentPid
‪int $contentPid
Definition: TypoScriptFrontendController.php:114
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$absRefPrefix
‪string $absRefPrefix
Definition: TypoScriptFrontendController.php:192
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\preparePageContentGeneration
‪preparePageContentGeneration(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:887
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\shouldAcquireCacheData
‪bool shouldAcquireCacheData(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:731
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$context
‪Context $context
Definition: TypoScriptFrontendController.php:266
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$debugInformationHeader
‪string $debugInformationHeader
Definition: TypoScriptFrontendController.php:272
‪TYPO3\CMS\Frontend\Event\ModifyTypoScriptConstantsEvent
Definition: ModifyTypoScriptConstantsEvent.php:29
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$newHash
‪string $newHash
Definition: TypoScriptFrontendController.php:164
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\releaseLocks
‪releaseLocks()
Definition: TypoScriptFrontendController.php:1476
‪TYPO3\CMS\Core\Http\PropagateResponseException
Definition: PropagateResponseException.php:47
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getFromCache
‪getFromCache(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:358
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getRelevantParametersForCachingFromPageArguments
‪getRelevantParametersForCachingFromPageArguments(PageArguments $pageArguments)
Definition: TypoScriptFrontendController.php:330
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Core\Core\Environment\getFrameworkBasePath
‪static getFrameworkBasePath()
Definition: Environment.php:245
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getWebsiteTitle
‪getWebsiteTitle(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1025
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$id
‪int $id
Definition: TypoScriptFrontendController.php:91
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\__construct
‪__construct()
Definition: TypoScriptFrontendController.php:277
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\addCacheTags
‪addCacheTags(array $tags)
Definition: TypoScriptFrontendController.php:872
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:82
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$recordRegister
‪array $recordRegister
Definition: TypoScriptFrontendController.php:212
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cacheGenerated
‪int $cacheGenerated
Definition: TypoScriptFrontendController.php:157
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$uniqueString
‪string $uniqueString
Definition: TypoScriptFrontendController.php:230
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getAdditionalHeaders
‪getAdditionalHeaders(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1484
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\createHashBase
‪string createHashBase(ServerRequestInterface $request, array $sysTemplateRows, array $constantConditionList, array $setupConditionList)
Definition: TypoScriptFrontendController.php:750
‪TYPO3\CMS\Core\TypoScript\AST\Node\ChildNode
Definition: ChildNode.php:23
‪TYPO3\CMS\Core\TypoScript\AST\Node\RootNode
Definition: RootNode.php:26
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cacheTimeOutDefault
‪int $cacheTimeOutDefault
Definition: TypoScriptFrontendController.php:146
‪TYPO3\CMS\Core\Localization\Locale
Definition: Locale.php:30
‪TYPO3\CMS\Frontend\Page\CacheHashCalculator
Definition: CacheHashCalculator.php:25
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\INTincScript_loadJSCode
‪INTincScript_loadJSCode()
Definition: TypoScriptFrontendController.php:1163
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\ConditionVerdictAwareIncludeTreeTraverser
Definition: ConditionVerdictAwareIncludeTreeTraverser.php:38
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setSysLastChanged
‪setSysLastChanged(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:844
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$lock
‪ResourceMutex $lock
Definition: TypoScriptFrontendController.php:250
‪TYPO3\CMS\Frontend\Controller
Definition: ErrorController.php:18
‪TYPO3\CMS\Core\Type\DocType
‪DocType
Definition: DocType.php:27
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:24
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cObj
‪ContentObjectRenderer $cObj
Definition: TypoScriptFrontendController.php:237
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\set_cache_timeout_default
‪set_cache_timeout_default(int $seconds)
Definition: TypoScriptFrontendController.php:1432
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$languageService
‪LanguageService $languageService
Definition: TypoScriptFrontendController.php:245
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\TypoScript\FrontendTypoScript
Definition: FrontendTypoScript.php:29
‪TYPO3\CMS\Frontend\Event\ShouldUseCachedPageDataIfAvailableEvent
Definition: ShouldUseCachedPageDataIfAvailableEvent.php:28
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$config
‪array $config
Definition: TypoScriptFrontendController.php:141
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$content
‪string $content
Definition: TypoScriptFrontendController.php:243
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$rootLine
‪array $rootLine
Definition: TypoScriptFrontendController.php:97
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:34
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getTimeTracker
‪getTimeTracker()
Definition: TypoScriptFrontendController.php:1516
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\sL
‪string sL(string $input)
Definition: TypoScriptFrontendController.php:1464
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\newCObj
‪newCObj(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1310
‪TYPO3\CMS\Core\TypoScript\Tokenizer\LossyTokenizer
Definition: LossyTokenizer.php:57
‪TYPO3\CMS\Core\TypoScript\IncludeTree\SysTemplateTreeBuilder
Definition: SysTemplateTreeBuilder.php:72
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons
Definition: PageAccessFailureReasons.php:25
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\set_no_cache
‪set_no_cache(string $reason='')
Definition: TypoScriptFrontendController.php:1389
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:817
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageCacheTags
‪array $pageCacheTags
Definition: TypoScriptFrontendController.php:254
‪TYPO3\CMS\Frontend\Event\AfterCachedPageIsPersistedEvent
Definition: AfterCachedPageIsPersistedEvent.php:32
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeConditionMatcherVisitor
Definition: IncludeTreeConditionMatcherVisitor.php:44
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\INTincScript
‪INTincScript(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1044