‪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;
46 
57 class ‪TypoScriptFrontendController implements LoggerAwareInterface
58 {
59  use LoggerAwareTrait;
60 
67  public int ‪$id;
68 
73  public array ‪$rootLine = [];
74 
81  public ?array ‪$page = [];
82 
90  public int ‪$contentPid = 0;
91 
97 
117  public array ‪$config = [];
118 
122  protected int ‪$cacheTimeOutDefault = 0;
123 
130 
135  public int ‪$cacheExpires = 0;
136 
140  public int ‪$cacheGenerated = 0;
141 
147  public string ‪$newHash = '';
148 
162  public array ‪$additionalHeaderData = [];
163 
168  public array ‪$additionalFooterData = [];
169 
175  public string ‪$absRefPrefix = '';
176 
180  public array ‪$register = [];
181 
187  public array ‪$registerStack = [];
188 
195  public array ‪$recordRegister = [];
196 
205  public string ‪$currentRecord = '';
206 
211  protected int ‪$uniqueCounter = 0;
212 
213  protected string ‪$uniqueString = '';
214 
221 
226  public string ‪$content = '';
227 
229 
232 
236  public array ‪$pageCacheTags = [];
237 
242  protected string ‪$contentType = 'text/html; charset=utf-8';
243 
249 
255  public ?string ‪$debugInformationHeader = null;
256 
260  public function ‪__construct()
261  {
262  $this->sys_page = GeneralUtility::makeInstance(PageRepository::class);
263  $this->context = GeneralUtility::makeInstance(Context::class);
264  $this->uniqueString = md5(microtime());
265  $cacheManager = GeneralUtility::makeInstance(CacheManager::class);
266  $this->pageCache = $cacheManager->getCache('pages');
267  }
268 
272  public function ‪initializePageRenderer(ServerRequestInterface $request): void
273  {
274  if ($this->pageRenderer !== null) {
275  return;
276  }
277  $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
278  $this->pageRenderer->setTemplateFile('EXT:frontend/Resources/Private/Templates/MainPage.html');
279  // As initPageRenderer could be called in constructor and for USER_INTs, this information is only set
280  // once - in order to not override any previous settings of PageRenderer.
281  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
282  if ($language->hasCustomTypo3Language()) {
283  $locale = GeneralUtility::makeInstance(Locales::class)->createLocale($language->getTypo3Language());
284  } else {
285  $locale = $language->getLocale();
286  }
287  $this->pageRenderer->setLanguage($locale);
288  }
289 
295  public function ‪initializeLanguageService(ServerRequestInterface $request): void
296  {
297  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
298  $this->languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)->createFromSiteLanguage($language);
299  }
300 
304  public function ‪setContentType(string ‪$contentType): void
305  {
306  $this->contentType = ‪$contentType;
307  }
308 
314  public function ‪isGeneratePage(): bool
315  {
317  }
318 
326  protected function ‪setPageCacheContent(
327  ServerRequestInterface $request,
328  string ‪$content,
329  array $INTincScript,
330  array $INTincScript_ext,
331  array $pageTitleCache,
332  int $expirationTstamp
333  ): array {
334  $pageInformation = $request->getAttribute('frontend.page.information');
335  $pageId = $pageInformation->getId();
336  $pageRecord = $pageInformation->getPageRecord();
337  $cacheData = [
338  'page_id' => $pageId,
339  'content' => ‪$content,
340  'contentType' => ‪$this->contentType,
341  'INTincScript' => $INTincScript,
342  'INTincScript_ext' => $INTincScript_ext,
343  'pageTitleCache' => $pageTitleCache,
344  'expires' => $expirationTstamp,
345  'tstamp' => ‪$GLOBALS['EXEC_TIME'],
346  ];
347  $this->cacheExpires = $expirationTstamp;
348  $this->pageCacheTags[] = 'pageId_' . $pageId;
349  // Respect the page cache when content of pid is shown
350  if ($pageId !== $pageInformation->getContentFromPid()) {
351  $this->pageCacheTags[] = 'pageId_' . $pageInformation->getContentFromPid();
352  }
353  if (!empty($pageRecord['cache_tags'])) {
354  $tags = ‪GeneralUtility::trimExplode(',', $pageRecord['cache_tags'], true);
355  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
356  }
357  $this->pageCacheTags = array_unique($this->pageCacheTags);
358  // Add the cache themselves as well, because they are fetched by getPageCacheTags()
359  $cacheData['cacheTags'] = ‪$this->pageCacheTags;
360  $this->pageCache->set($this->newHash, $cacheData, $this->pageCacheTags, $expirationTstamp - ‪$GLOBALS['EXEC_TIME']);
361  return $cacheData;
362  }
363 
374  protected function ‪setSysLastChanged(ServerRequestInterface $request): void
375  {
376  // Only update if browsing the live workspace
377  $isInWorkspace = $this->context->getPropertyFromAspect('workspace', 'isOffline', false);
378  if ($isInWorkspace) {
379  return;
380  }
381  $pageInformation = $request->getAttribute('frontend.page.information');
382  $pageRecord = $pageInformation->getPageRecord();
383  if ($pageRecord['SYS_LASTCHANGED'] < (int)($this->register['SYS_LASTCHANGED'] ?? 0)) {
384  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
385  $pageId = $pageRecord['_LOCALIZED_UID'] ?? $pageInformation->getId();
386  $connection->update(
387  'pages',
388  [
389  'SYS_LASTCHANGED' => (int)$this->register['SYS_LASTCHANGED'],
390  ],
391  [
392  'uid' => (int)$pageId,
393  ]
394  );
395  }
396  }
397 
402  public function ‪addCacheTags(array $tags): void
403  {
404  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
405  }
406 
407  public function ‪getPageCacheTags(): array
408  {
410  }
411 
417  public function ‪preparePageContentGeneration(ServerRequestInterface $request): void
418  {
419  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
420  // calculate the absolute path prefix
421  if (!empty($this->absRefPrefix = trim($typoScriptConfigArray['absRefPrefix']))) {
422  if ($this->absRefPrefix === 'auto') {
423  $normalizedParams = $request->getAttribute('normalizedParams');
424  $this->absRefPrefix = $normalizedParams->getSitePath();
425  }
426  }
427  // config.forceAbsoluteUrls will override absRefPrefix
428  if ($typoScriptConfigArray['forceAbsoluteUrls'] ?? false) {
429  $normalizedParams = $request->getAttribute('normalizedParams');
430  $this->absRefPrefix = $normalizedParams->getSiteUrl();
431  }
432 
433  $docType = DocType::createFromConfigurationKey($typoScriptConfigArray['doctype']);
434  $this->pageRenderer->setDocType($docType);
435 
436  // Global content object
437  $this->‪newCObj($request);
438  }
439 
446  public function ‪generatePage_postProcessing(ServerRequestInterface $request): void
447  {
448  $this->‪setAbsRefPrefix();
449  $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
450  $usePageCache = $request->getAttribute('frontend.cache.instruction')->isCachingAllowed();
451  $event = new ‪AfterCacheableContentIsGeneratedEvent($request, $this, $this->newHash, $usePageCache);
452  $event = $eventDispatcher->dispatch($event);
453 
454  // Processing if caching is enabled
455  if ($event->isCachingEnabled()) {
456  // Seconds until a cached page is too old
457  $cacheTimeout = $this->‪get_cache_timeout($request);
458  $timeOutTime = ‪$GLOBALS['EXEC_TIME'] + $cacheTimeout;
459  // Write the page to cache, but do not cache localRootLine since that is always determined
460  // and coming from PageInformation->getLocalRootLine().
461  $cachedInformation = $this->‪setPageCacheContent(
462  $request,
463  $this->content,
464  $this->config['INTincScript'] ?? [],
465  $this->config['INTincScript_ext'] ?? [],
466  $this->config['pageTitleCache'] ?? [],
467  $timeOutTime
468  );
469  // Event for cache post processing (eg. writing static files)
470  $event = new ‪AfterCachedPageIsPersistedEvent($request, $this, $this->newHash, $cachedInformation, $cacheTimeout);
471  $eventDispatcher->dispatch($event);
472  }
473  $this->‪setSysLastChanged($request);
474  }
475 
482  public function ‪generatePageTitle(ServerRequestInterface $request): string
483  {
484  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
485  // config.noPageTitle = 2 - means do not render the page title
486  if ((int)($typoScriptConfigArray['noPageTitle'] ?? 0) === 2) {
487  return '';
488  }
489 
490  // Check for a custom pageTitleSeparator, and perform stdWrap on it
491  $pageTitleSeparator = (string)$this->cObj->stdWrapValue('pageTitleSeparator', $typoScriptConfigArray);
492  if ($pageTitleSeparator !== '' && $pageTitleSeparator === ($typoScriptConfigArray['pageTitleSeparator'] ?? '')) {
493  $pageTitleSeparator .= ' ';
494  }
495 
496  $titleProvider = GeneralUtility::makeInstance(PageTitleProviderManager::class);
497  if (!empty($this->config['pageTitleCache'])) {
498  $titleProvider->setPageTitleCache($this->config['pageTitleCache']);
499  }
500  $pageTitle = $titleProvider->getTitle($request);
501  $this->config['pageTitleCache'] = $titleProvider->getPageTitleCache();
502 
503  $titleTagContent = $this->‪printTitle(
504  $request,
505  $pageTitle,
506  (bool)($typoScriptConfigArray['noPageTitle'] ?? false),
507  (bool)($typoScriptConfigArray['pageTitleFirst'] ?? false),
508  $pageTitleSeparator,
509  (bool)($typoScriptConfigArray['showWebsiteTitle'] ?? true)
510  );
511 
512  if (isset($typoScriptConfigArray['pageTitle.']) && is_array($typoScriptConfigArray['pageTitle.'])) {
513  // stdWrap for pageTitle if set in config.pageTitle.
514  $pageTitleStdWrapArray = [
515  'pageTitle' => $titleTagContent,
516  'pageTitle.' => $typoScriptConfigArray['pageTitle.'],
517  ];
518  $titleTagContent = $this->cObj->stdWrapValue('pageTitle', $pageTitleStdWrapArray);
519  }
520 
521  if ($titleTagContent !== '') {
522  $this->pageRenderer->setTitle($titleTagContent);
523  }
524  return (string)$titleTagContent;
525  }
526 
538  protected function ‪printTitle(ServerRequestInterface $request, string $pageTitle, bool $noPageTitle = false, bool $showPageTitleFirst = false, string $pageTitleSeparator = '', bool $showWebsiteTitle = true): string
539  {
540  $websiteTitle = $showWebsiteTitle ? $this->‪getWebsiteTitle($request) : '';
541  $pageTitle = $noPageTitle ? '' : $pageTitle;
542  // only show a separator if there are both site title and page title
543  if ($pageTitle === '' || $websiteTitle === '') {
544  $pageTitleSeparator = '';
545  } elseif (empty($pageTitleSeparator)) {
546  // use the default separator if non given
547  $pageTitleSeparator = ': ';
548  }
549  if ($showPageTitleFirst) {
550  return $pageTitle . $pageTitleSeparator . $websiteTitle;
551  }
552  return $websiteTitle . $pageTitleSeparator . $pageTitle;
553  }
554 
555  protected function ‪getWebsiteTitle(ServerRequestInterface $request): string
556  {
557  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
558  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
559  if (trim($language->getWebsiteTitle()) !== '') {
560  return trim($language->getWebsiteTitle());
561  }
562  $siteConfiguration = $request->getAttribute('site')->getConfiguration();
563  if (trim($siteConfiguration['websiteTitle'] ?? '') !== '') {
564  return trim($siteConfiguration['websiteTitle']);
565  }
566  return '';
567  }
568 
574  public function ‪INTincScript(ServerRequestInterface $request): void
575  {
576  $this->additionalHeaderData = $this->config['INTincScript_ext']['additionalHeaderData'] ?? [];
577  $this->additionalFooterData = $this->config['INTincScript_ext']['additionalFooterData'] ?? [];
578  if (empty($this->config['INTincScript_ext']['pageRendererState'])) {
579  $this->‪initializePageRenderer($request);
580  } else {
581  $pageRendererState = unserialize($this->config['INTincScript_ext']['pageRendererState'], ['allowed_classes' => [Locale::class]]);
582  $this->pageRenderer->updateState($pageRendererState);
583  }
584  if (!empty($this->config['INTincScript_ext']['assetCollectorState'])) {
585  $assetCollectorState = unserialize($this->config['INTincScript_ext']['assetCollectorState'], ['allowed_classes' => false]);
586  GeneralUtility::makeInstance(AssetCollector::class)->updateState($assetCollectorState);
587  }
588 
590  $this->‪getTimeTracker()->push('Substitute header section');
592  $this->‪generatePageTitle($request);
593 
594  $this->content = str_replace(
595  [
596  '<!--HD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
597  '<!--FD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
598  ],
599  [
600  implode(LF, $this->additionalHeaderData),
601  implode(LF, $this->additionalFooterData),
602  ],
603  $this->pageRenderer->renderJavaScriptAndCssForProcessingOfUncachedContentObjects($this->content, $this->config['INTincScript_ext']['divKey'])
604  );
605  // Replace again, because header and footer data and page renderer replacements may introduce additional placeholders (see #44825)
607  $this->‪setAbsRefPrefix();
608  $this->‪getTimeTracker()->pull();
609  }
610 
616  protected function ‪recursivelyReplaceIntPlaceholdersInContent(ServerRequestInterface $request): void
617  {
618  do {
619  $nonCacheableData = $this->config['INTincScript'];
620  $this->‪processNonCacheableContentPartsAndSubstituteContentMarkers($nonCacheableData, $request);
621  // Check if there were new items added to INTincScript during the previous execution:
622  // array_diff_assoc throws notices if values are arrays but not strings. We suppress this here.
623  $nonCacheableData = @array_diff_assoc($this->config['INTincScript'], $nonCacheableData);
624  $reprocess = count($nonCacheableData) > 0;
625  } while ($reprocess);
626  }
627 
637  protected function ‪processNonCacheableContentPartsAndSubstituteContentMarkers(array $nonCacheableData, ServerRequestInterface $request): void
638  {
639  $timeTracker = $this->‪getTimeTracker();
640  $timeTracker->push('Split content');
641  // Splits content with the key.
642  $contentSplitByUncacheableMarkers = explode('<!--INT_SCRIPT.', $this->content);
643  $this->content = '';
644  $timeTracker->setTSlogMessage('Parts: ' . count($contentSplitByUncacheableMarkers), LogLevel::INFO);
645  $timeTracker->pull();
646  foreach ($contentSplitByUncacheableMarkers as $counter => $contentPart) {
647  // If the split had a comment-end after 32 characters it's probably a split-string
648  if (substr($contentPart, 32, 3) === '-->') {
649  $nonCacheableKey = 'INT_SCRIPT.' . substr($contentPart, 0, 32);
650  if (is_array($nonCacheableData[$nonCacheableKey] ?? false)) {
651  $label = 'Include ' . $nonCacheableData[$nonCacheableKey]['type'];
652  $timeTracker->push($label);
653  $nonCacheableContent = '';
654  $contentObjectRendererForNonCacheable = unserialize($nonCacheableData[$nonCacheableKey]['cObj']);
655  if ($contentObjectRendererForNonCacheable instanceof ‪ContentObjectRenderer) {
656  $contentObjectRendererForNonCacheable->setRequest($request);
657  $nonCacheableContent = match ($nonCacheableData[$nonCacheableKey]['type']) {
658  'COA' => $contentObjectRendererForNonCacheable->cObjGetSingle('COA', $nonCacheableData[$nonCacheableKey]['conf']),
659  'FUNC' => $contentObjectRendererForNonCacheable->cObjGetSingle('USER', $nonCacheableData[$nonCacheableKey]['conf']),
660  'POSTUSERFUNC' => $contentObjectRendererForNonCacheable->callUserFunction($nonCacheableData[$nonCacheableKey]['postUserFunc'], $nonCacheableData[$nonCacheableKey]['conf'], $nonCacheableData[$nonCacheableKey]['content']),
661  default => '',
662  };
663  }
664  $this->content .= $nonCacheableContent;
665  $this->content .= substr($contentPart, 35);
666  $timeTracker->pull($nonCacheableContent);
667  } else {
668  $this->content .= substr($contentPart, 35);
669  }
670  } elseif ($counter) {
671  // If it's not the first entry (which would be "0" of the array keys), then re-add the INT_SCRIPT part
672  $this->content .= '<!--INT_SCRIPT.' . $contentPart;
673  } else {
674  $this->content .= $contentPart;
675  }
676  }
677  // invokes permanent, general handlers
678  foreach ($nonCacheableData as $item) {
679  if (empty($item['permanent']) || empty($item['target'])) {
680  continue;
681  }
682  $parameters = array_merge($item['parameters'] ?? [], ['content' => $this->content]);
683  $this->content = GeneralUtility::callUserFunction($item['target'], $parameters) ?? ‪$this->content;
684  }
685  }
686 
693  public function ‪INTincScript_loadJSCode(): void
694  {
695  // Prepare code and placeholders for additional header and footer files (and make sure that this isn't called twice)
696  if ($this->‪isINTincScript() && (!isset($this->config['INTincScript_ext']) || $this->config['INTincScript_ext'] === [])) {
697  $substituteHash = $this->‪uniqueHash();
698  $this->config['INTincScript_ext']['divKey'] = $substituteHash;
699  // Storing the header-data array
700  $this->config['INTincScript_ext']['additionalHeaderData'] = ‪$this->additionalHeaderData;
701  // Storing the footer-data array
702  $this->config['INTincScript_ext']['additionalFooterData'] = ‪$this->additionalFooterData;
703  // Clearing the array
704  $this->additionalHeaderData = ['<!--HD_' . $substituteHash . '-->'];
705  // Clearing the array
706  $this->additionalFooterData = ['<!--FD_' . $substituteHash . '-->'];
707  }
708  }
709 
716  public function ‪isINTincScript(): bool
717  {
718  return !empty($this->config['INTincScript']) && is_array($this->config['INTincScript']);
719  }
720 
726  public function ‪applyHttpHeadersToResponse(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
727  {
728  $response = $response->withHeader('Content-Type', $this->contentType);
729  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
730  if (empty($typoScriptConfigArray['disableLanguageHeader'])) {
731  // Set header for content language unless disabled
732  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
733  $language = $request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage();
734  $response = $response->withHeader('Content-Language', (string)$language->getLocale());
735  }
736 
737  // Add a Response header to show debug information if a page was fetched from cache
738  if ($this->debugInformationHeader) {
739  $response = $response->withHeader('X-TYPO3-Debug-Cache', $this->debugInformationHeader);
740  }
741 
742  // Set cache related headers to client (used to enable proxy / client caching!)
743  $headers = $this->‪getCacheHeaders($request);
744  foreach ($headers as $header => $value) {
745  $response = $response->withHeader($header, $value);
746  }
747  // Set additional headers if any have been configured via TypoScript
748  $additionalHeaders = $this->‪getAdditionalHeaders($request);
749  foreach ($additionalHeaders as $headerConfig) {
750  [$header, $value] = ‪GeneralUtility::trimExplode(':', $headerConfig['header'], false, 2);
751  if ($headerConfig['statusCode']) {
752  $response = $response->withStatus((int)$headerConfig['statusCode']);
753  }
754  if ($headerConfig['replace']) {
755  $response = $response->withHeader($header, $value);
756  } else {
757  $response = $response->withAddedHeader($header, $value);
758  }
759  }
760  return $response;
761  }
762 
766  protected function ‪getCacheHeaders(ServerRequestInterface $request): array
767  {
768  $headers = [];
769  // Getting status whether we can send cache control headers for proxy caching:
770  $doCache = $this->‪isStaticCacheble($request);
771  $isBackendUserLoggedIn = $this->context->getPropertyFromAspect('backend.user', 'isLoggedIn', false);
772  $isInWorkspace = $this->context->getPropertyFromAspect('workspace', 'isOffline', false);
773  // Finally, when backend users are logged in, do not send cache headers at all (Admin Panel might be displayed for instance).
774  $isClientCachable = $doCache && !$isBackendUserLoggedIn && !$isInWorkspace;
775  if ($isClientCachable) {
776  // Only send the headers to the client that they are allowed to cache if explicitly activated.
777  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
778  if (!empty($typoScriptConfigArray['sendCacheHeaders'])) {
779  $headers = [
780  'Expires' => gmdate('D, d M Y H:i:s T', $this->cacheExpires),
781  'ETag' => '"' . md5($this->content) . '"',
782  'Cache-Control' => 'max-age=' . ($this->cacheExpires - ‪$GLOBALS['EXEC_TIME']),
783  // no-cache
784  'Pragma' => 'public',
785  ];
786  }
787  } else {
788  // "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
789  $headers = [
790  'Cache-Control' => 'private, no-store',
791  ];
792  // Now, if a backend user is logged in, tell him in the Admin Panel log what the caching status would have been:
793  if ($isBackendUserLoggedIn) {
794  if ($doCache) {
795  $this->‪getTimeTracker()->setTSlogMessage('Cache-headers with max-age "' . ($this->cacheExpires - ‪$GLOBALS['EXEC_TIME']) . '" would have been sent');
796  } else {
797  $reasonMsg = [];
798  if (!$request->getAttribute('frontend.cache.instruction')->isCachingAllowed()) {
799  $reasonMsg[] = 'Caching disabled.';
800  }
801  if ($this->‪isINTincScript()) {
802  $reasonMsg[] = '*_INT object(s) on page.';
803  }
804  if ($this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn', false)) {
805  $reasonMsg[] = 'Frontend user logged in.';
806  }
807  $this->‪getTimeTracker()->setTSlogMessage('Cache-headers would disable proxy caching! Reason(s): "' . implode(' ', $reasonMsg) . '"', LogLevel::NOTICE);
808  }
809  }
810  }
811  return $headers;
812  }
813 
828  public function ‪isStaticCacheble(ServerRequestInterface $request): bool
829  {
830  $isCachingAllowed = $request->getAttribute('frontend.cache.instruction')->isCachingAllowed();
831  return $isCachingAllowed && !$this->‪isINTincScript() && !$this->context->getAspect('frontend.user')->isUserOrGroupSet();
832  }
833 
840  public function ‪newCObj(ServerRequestInterface $request): void
841  {
842  $this->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $this);
843  $this->cObj->setRequest($request);
844  $this->cObj->start($request->getAttribute('frontend.page.information')->getPageRecord(), 'pages');
845  }
846 
853  protected function ‪setAbsRefPrefix(): void
854  {
855  if (!$this->absRefPrefix) {
856  return;
857  }
858  $encodedAbsRefPrefix = htmlspecialchars($this->absRefPrefix, ENT_QUOTES | ENT_HTML5);
859  $search = [
860  '"_assets/',
861  '"typo3temp/',
864  ];
865  $replace = [
866  '"' . $encodedAbsRefPrefix . '_assets/',
867  '"' . $encodedAbsRefPrefix . 'typo3temp/',
868  '"' . $encodedAbsRefPrefix . ‪PathUtility::stripPathSitePrefix(‪Environment::getExtensionsPath()) . '/',
870  ];
871  // Process additional directories
872  $directories = ‪GeneralUtility::trimExplode(',', ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalAbsRefPrefixDirectories'], true);
873  foreach ($directories as $directory) {
874  $search[] = '"' . $directory;
875  $replace[] = '"' . $encodedAbsRefPrefix . $directory;
876  }
877  $this->content = str_replace(
878  $search,
879  $replace,
880  $this->content
881  );
882  }
883 
893  public function ‪logDeprecatedTyposcript(string $typoScriptProperty, string $explanation = ''): void
894  {
895  $explanationText = $explanation !== '' ? ' - ' . $explanation : '';
896  $this->‪getTimeTracker()->setTSlogMessage($typoScriptProperty . ' is deprecated.' . $explanationText, LogLevel::WARNING);
897  trigger_error('TypoScript property ' . $typoScriptProperty . ' is deprecated' . $explanationText, E_USER_DEPRECATED);
898  }
899 
908  public function ‪uniqueHash(string $str = ''): string
909  {
910  return md5($this->uniqueString . '_' . $str . $this->uniqueCounter++);
911  }
912 
919  public function ‪set_no_cache(string $reason = ''): void
920  {
921  $warning = '';
922  ‪$context = [];
923  if ($reason !== '') {
924  $warning = '$TSFE->set_no_cache() was triggered. Reason: {reason}.';
925  ‪$context['reason'] = $reason;
926  } else {
927  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
928  if (isset($trace[0]['class'])) {
929  ‪$context['class'] = $trace[0]['class'];
930  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {class} on line {line}.';
931  }
932  if (isset($trace[0]['function'])) {
933  ‪$context['function'] = $trace[0]['function'];
934  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {class}->{function} on line {line}.';
935  }
936  if (‪$context === []) {
937  // Only store the filename, not the full path for safety reasons
938  ‪$context['file'] = basename($trace[0]['file']);
939  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {file} on line {line}.';
940  }
941  ‪$context['line'] = $trace[0]['line'];
942  }
943  if (‪$GLOBALS['TYPO3_CONF_VARS']['FE']['disableNoCacheParameter']) {
944  $warning .= ' However, $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set, so it will be ignored!';
945  $this->‪getTimeTracker()->setTSlogMessage($warning, LogLevel::NOTICE);
946  } else {
947  $warning .= ' Caching is disabled!';
949  $request = ‪$GLOBALS['TYPO3_REQUEST'];
950  $cacheInstruction = $request->getAttribute('frontend.cache.instruction');
951  $cacheInstruction->disableCache('EXT:frontend: Caching disabled using deprecated set_no_cache().');
952  }
953  $this->logger->notice($warning, ‪$context);
954  }
955 
962  public function ‪set_cache_timeout_default(int $seconds): void
963  {
964  $seconds = (int)$seconds;
965  if ($seconds > 0) {
966  $this->cacheTimeOutDefault = $seconds;
967  }
968  }
969 
973  protected function ‪get_cache_timeout(ServerRequestInterface $request): int
974  {
975  $pageInformation = $request->getAttribute('frontend.page.information');
976  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
977  return GeneralUtility::makeInstance(CacheLifetimeCalculator::class)
978  ->calculateLifetimeForPage(
979  $pageInformation->getId(),
980  $pageInformation->getPageRecord(),
981  $typoScriptConfigArray,
982  $this->cacheTimeOutDefault,
983  $this->context
984  );
985  }
986 
994  public function ‪sL(string $input): string
995  {
996  return $this->languageService->sL($input);
997  }
998 
1002  protected function ‪getAdditionalHeaders(ServerRequestInterface $request): array
1003  {
1004  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
1005  if (!isset($typoScriptConfigArray['additionalHeaders.'])) {
1006  return [];
1007  }
1008  $additionalHeaders = [];
1009  $additionalHeadersConfig = $typoScriptConfigArray['additionalHeaders.'];
1010  ksort($additionalHeadersConfig);
1011  foreach ($additionalHeadersConfig as $options) {
1012  if (!is_array($options)) {
1013  continue;
1014  }
1015  $header = trim($options['header'] ?? '');
1016  if ($header === '') {
1017  continue;
1018  }
1019  $additionalHeaders[] = [
1020  'header' => $header,
1021  // "replace existing headers" is turned on by default, unless turned off
1022  'replace' => ($options['replace'] ?? '') !== '0',
1023  'statusCode' => (int)($options['httpResponseCode'] ?? 0) ?: null,
1024  ];
1025  }
1026  return $additionalHeaders;
1027  }
1028 
1030  {
1031  return ‪$GLOBALS['BE_USER'] ?? null;
1032  }
1033 
1034  protected function ‪getTimeTracker(): ‪TimeTracker
1035  {
1036  return GeneralUtility::makeInstance(TimeTracker::class);
1037  }
1038 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setAbsRefPrefix
‪setAbsRefPrefix()
Definition: TypoScriptFrontendController.php:853
‪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:908
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$contentType
‪string $contentType
Definition: TypoScriptFrontendController.php:242
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$uniqueCounter
‪int $uniqueCounter
Definition: TypoScriptFrontendController.php:211
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$currentRecord
‪string $currentRecord
Definition: TypoScriptFrontendController.php:205
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\generatePageTitle
‪generatePageTitle(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:482
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\recursivelyReplaceIntPlaceholdersInContent
‪recursivelyReplaceIntPlaceholdersInContent(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:616
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cacheExpires
‪int $cacheExpires
Definition: TypoScriptFrontendController.php:135
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initializeLanguageService
‪initializeLanguageService(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:295
‪TYPO3\CMS\Backend\FrontendBackendUserAuthentication
Definition: FrontendBackendUserAuthentication.php:29
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isGeneratePage
‪isGeneratePage()
Definition: TypoScriptFrontendController.php:314
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$sys_page
‪PageRepository $sys_page
Definition: TypoScriptFrontendController.php:96
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isStaticCacheble
‪isStaticCacheble(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:828
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$register
‪array $register
Definition: TypoScriptFrontendController.php:180
‪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:538
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setPageCacheContent
‪setPageCacheContent(ServerRequestInterface $request, string $content, array $INTincScript, array $INTincScript_ext, array $pageTitleCache, int $expirationTstamp)
Definition: TypoScriptFrontendController.php:326
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$additionalFooterData
‪array $additionalFooterData
Definition: TypoScriptFrontendController.php:168
‪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:304
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageContentWasLoadedFromCache
‪bool $pageContentWasLoadedFromCache
Definition: TypoScriptFrontendController.php:129
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$page
‪array $page
Definition: TypoScriptFrontendController.php:81
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$registerStack
‪array $registerStack
Definition: TypoScriptFrontendController.php:187
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\logDeprecatedTyposcript
‪logDeprecatedTyposcript(string $typoScriptProperty, string $explanation='')
Definition: TypoScriptFrontendController.php:893
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$additionalHeaderData
‪array $additionalHeaderData
Definition: TypoScriptFrontendController.php:162
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\generatePage_postProcessing
‪generatePage_postProcessing(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:446
‪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:716
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\processNonCacheableContentPartsAndSubstituteContentMarkers
‪processNonCacheableContentPartsAndSubstituteContentMarkers(array $nonCacheableData, ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:637
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\get_cache_timeout
‪get_cache_timeout(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:973
‪TYPO3\CMS\Frontend\Cache\CacheLifetimeCalculator
Definition: CacheLifetimeCalculator.php:39
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initializePageRenderer
‪initializePageRenderer(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:272
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageRenderer
‪PageRenderer $pageRenderer
Definition: TypoScriptFrontendController.php:230
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageCache
‪FrontendInterface $pageCache
Definition: TypoScriptFrontendController.php:231
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getCacheHeaders
‪getCacheHeaders(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:766
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\applyHttpHeadersToResponse
‪applyHttpHeadersToResponse(ServerRequestInterface $request, ResponseInterface $response)
Definition: TypoScriptFrontendController.php:726
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getPageCacheTags
‪getPageCacheTags()
Definition: TypoScriptFrontendController.php:407
‪TYPO3\CMS\Core\PageTitle\PageTitleProviderManager
Definition: PageTitleProviderManager.php:33
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getBackendUser
‪getBackendUser()
Definition: TypoScriptFrontendController.php:1029
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$contentPid
‪int $contentPid
Definition: TypoScriptFrontendController.php:90
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$absRefPrefix
‪string $absRefPrefix
Definition: TypoScriptFrontendController.php:175
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\preparePageContentGeneration
‪preparePageContentGeneration(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:417
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$context
‪Context $context
Definition: TypoScriptFrontendController.php:248
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$debugInformationHeader
‪string $debugInformationHeader
Definition: TypoScriptFrontendController.php:255
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$newHash
‪string $newHash
Definition: TypoScriptFrontendController.php:147
‪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:555
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$id
‪int $id
Definition: TypoScriptFrontendController.php:67
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\__construct
‪__construct()
Definition: TypoScriptFrontendController.php:260
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\addCacheTags
‪addCacheTags(array $tags)
Definition: TypoScriptFrontendController.php:402
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:58
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$recordRegister
‪array $recordRegister
Definition: TypoScriptFrontendController.php:195
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cacheGenerated
‪int $cacheGenerated
Definition: TypoScriptFrontendController.php:140
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$uniqueString
‪string $uniqueString
Definition: TypoScriptFrontendController.php:213
‪$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:1002
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cacheTimeOutDefault
‪int $cacheTimeOutDefault
Definition: TypoScriptFrontendController.php:122
‪TYPO3\CMS\Core\Localization\Locale
Definition: Locale.php:30
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\INTincScript_loadJSCode
‪INTincScript_loadJSCode()
Definition: TypoScriptFrontendController.php:693
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setSysLastChanged
‪setSysLastChanged(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:374
‪TYPO3\CMS\Frontend\Controller
Definition: ErrorController.php:18
‪TYPO3\CMS\Core\Type\DocType
‪DocType
Definition: DocType.php:27
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$cObj
‪ContentObjectRenderer $cObj
Definition: TypoScriptFrontendController.php:220
‪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:962
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$languageService
‪LanguageService $languageService
Definition: TypoScriptFrontendController.php:228
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$config
‪array $config
Definition: TypoScriptFrontendController.php:117
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$content
‪string $content
Definition: TypoScriptFrontendController.php:226
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$rootLine
‪array $rootLine
Definition: TypoScriptFrontendController.php:73
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:34
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getTimeTracker
‪getTimeTracker()
Definition: TypoScriptFrontendController.php:1034
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\sL
‪string sL(string $input)
Definition: TypoScriptFrontendController.php:994
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\newCObj
‪newCObj(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:840
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\set_no_cache
‪set_no_cache(string $reason='')
Definition: TypoScriptFrontendController.php:919
‪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\Frontend\Controller\TypoScriptFrontendController\$pageCacheTags
‪array $pageCacheTags
Definition: TypoScriptFrontendController.php:236
‪TYPO3\CMS\Frontend\Event\AfterCachedPageIsPersistedEvent
Definition: AfterCachedPageIsPersistedEvent.php:32
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\INTincScript
‪INTincScript(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:574