‪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;
90 
101 class ‪TypoScriptFrontendController implements LoggerAwareInterface
102 {
103  use LoggerAwareTrait;
104 
108  public int ‪$id;
109 
115  protected int|string ‪$type = 0;
116 
117  protected ‪Site ‪$site;
119 
124 
131  public ‪$no_cache = false;
132 
157  public array ‪$rootLine = [];
158 
163  public ‪$page = [];
164 
169  public int ‪$contentPid = 0;
170 
176  protected ?array ‪$originalMountPointPage = null;
177 
182  protected ?array ‪$originalShortcutPage = null;
183 
189  public ‪$sys_page = '';
190 
194  protected int ‪$pageNotFound = 0;
195 
199  protected array ‪$pageAccessFailureHistory = [];
200 
205  public ‪$MP = '';
206 
212  public ‪$fe_user;
213 
246  public $config = [];
247 
253  protected int $cacheTimeOutDefault = 0;
254 
259  protected bool $pageContentWasLoadedFromCache = false;
260 
265  protected int $cacheExpires = 0;
266 
272  public $pSetup = '';
273 
279  public string $newHash = '';
280 
288  protected bool $no_cacheBeforePageGen = false;
289 
303  public $additionalHeaderData = [];
304 
309  public $additionalFooterData = [];
310 
315  public $absRefPrefix = '';
316 
323  public string $linkVars = '';
324 
329  public array $applicationData = [];
330 
331  public array $register = [];
332 
337  public array $registerStack = [];
338 
343  public array $recordRegister = [];
344 
351  public string $currentRecord = '';
352 
358  protected int $uniqueCounter = 0;
359 
363  protected string $uniqueString = '';
364 
370  public $cObj;
371 
376  public $content = '';
377 
382  public ?array $lastImgResourceInfo = null;
383 
387  protected ?‪LanguageService $languageService = null;
388 
392  public ?‪ResourceMutex $lock = null;
393 
394  protected ?‪PageRenderer $pageRenderer = null;
395 
402  protected $pageCache;
403 
404  protected array $pageCacheTags = [];
405 
411  protected string $contentType = 'text/html; charset=utf-8';
412 
416  protected int $requestedId = 0;
417 
422  protected ‪Context $context;
423 
428  protected string $debugInformationHeader = '';
429 
445  public function __construct(‪Context $context, ‪Site ‪$site, ‪SiteLanguage $siteLanguage, ‪PageArguments ‪$pageArguments, ‪FrontendUserAuthentication $frontendUser)
446  {
447  $this->‪initializeContext($context);
448  $this->‪site = ‪$site;
449  $this->‪language = $siteLanguage;
450  $this->‪setPageArguments($pageArguments);
451  $this->‪fe_user = $frontendUser;
452  $this->‪uniqueString = md5(microtime());
453  $this->‪initPageRenderer();
454  $this->‪initCaches();
455  }
456 
457  private function ‪initializeContext(‪Context $context): void
458  {
459  $this->context = $context;
460  if (!$this->context->hasAspect('frontend.preview')) {
461  $this->context->‪setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class));
462  }
463  }
464 
468  protected function ‪initPageRenderer()
469  {
470  if ($this->pageRenderer !== null) {
471  return;
472  }
473  $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
474  $this->pageRenderer->setTemplateFile('EXT:frontend/Resources/Private/Templates/MainPage.html');
475  // As initPageRenderer could be called in constructor and for USER_INTs, this information is only set
476  // once - in order to not override any previous settings of PageRenderer.
477  if ($this->‪language->hasCustomTypo3Language()) {
478  $locale = GeneralUtility::makeInstance(Locales::class)->createLocale($this->‪language->getTypo3Language());
479  } else {
480  $locale = $this->‪language->getLocale();
481  }
482  $this->pageRenderer->setLanguage($locale);
483  }
484 
489  public function ‪setContentType($contentType)
490  {
491  $this->contentType = $contentType;
492  }
493 
494  /********************************************
495  *
496  * Initializing, resolving page id
497  *
498  ********************************************/
502  protected function ‪initCaches()
503  {
504  $cacheManager = GeneralUtility::makeInstance(CacheManager::class);
505  $this->pageCache = $cacheManager->getCache('pages');
506  }
507 
540  public function ‪determineId(ServerRequestInterface $request): ?ResponseInterface
541  {
542  $this->sys_page = GeneralUtility::makeInstance(PageRepository::class, $this->context);
543 
544  $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
545  $eventDispatcher->dispatch(new BeforePageIsResolvedEvent($this, $request));
546 
547  $timeTracker = $this->‪getTimeTracker();
548  $timeTracker->push('determineId rootLine/');
549  try {
550  // Sets ->page and ->rootline information based on ->id. ->id may change during this operation.
551  // If the found Page ID is not within the site, then pageNotFound is set.
552  $this->‪getPageAndRootline($request);
553  // Checks if the rootPageId of the site is in the resolved rootLine.
554  // This is necessary so that references to page-id's via ?id=123 from other sites are not possible.
555  $siteRootWithinRootlineFound = false;
556  foreach ($this->rootLine as $pageInRootLine) {
557  if ((int)$pageInRootLine['uid'] === $this->‪site->getRootPageId()) {
558  $siteRootWithinRootlineFound = true;
559  break;
560  }
561  }
562  // Page is 'not found' in case the id was outside the domain, code 3
563  // This can only happen if there was a shortcut. So $this->page is now the shortcut target
564  // But the original page is in $this->originalShortcutPage.
565  // This only happens if people actually call TYPO3 with index.php?id=123 where 123 is in a different
566  // page tree. This is not allowed.
567  $directlyRequestedId = (int)($request->getQueryParams()['id'] ?? 0);
568  if (!$siteRootWithinRootlineFound && $directlyRequestedId && (int)($this->originalShortcutPage['uid'] ?? 0) !== $directlyRequestedId) {
569  $this->pageNotFound = 3;
570  $this->id = $this->‪site->getRootPageId();
571  // re-get the page and rootline if the id was not found.
572  $this->‪getPageAndRootline($request);
573  }
574  } catch (ShortcutTargetPageNotFoundException $e) {
575  $this->pageNotFound = 1;
576  }
577  $timeTracker->pull();
578 
579  $event = new AfterPageWithRootLineIsResolvedEvent($this, $request);
580  $event = $eventDispatcher->dispatch($event);
581  if ($event->getResponse()) {
582  return $event->getResponse();
583  }
584 
585  $response = null;
586  try {
587  $this->‪evaluatePageNotFound($this->pageNotFound, $request);
588 
589  // Setting language and fetch translated page
590  $this->‪settingLanguage($request);
591  // Check the "content_from_pid" field of the resolved page
592  $this->contentPid = $this->‪resolveContentPid($request);
593 
594  // Update SYS_LASTCHANGED at the very last, when $this->page might be changed
595  // by settingLanguage() and the $this->page was finally resolved
597  } catch (‪PropagateResponseException $e) {
598  $response = $e->‪getResponse();
599  }
600 
601  $event = new AfterPageAndLanguageIsResolvedEvent($this, $request, $response);
602  $eventDispatcher->dispatch($event);
603  return $event->getResponse();
604  }
605 
609  protected function ‪evaluatePageNotFound(int $pageNotFoundNumber, ServerRequestInterface $request): void
610  {
611  if (!$pageNotFoundNumber) {
612  return;
613  }
614  $response = match ($pageNotFoundNumber) {
615  1 => GeneralUtility::makeInstance(ErrorController::class)->accessDeniedAction(
616  $request,
617  'ID was not an accessible page',
619  ),
620  2 => GeneralUtility::makeInstance(ErrorController::class)->accessDeniedAction(
621  $request,
622  'Subsection was found and not accessible',
624  ),
625  3 => GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
626  $request,
627  'ID was outside the domain',
629  ),
630  default => GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
631  $request,
632  'Unspecified error',
634  ),
635  };
636  throw new PropagateResponseException($response, 1533931329);
637  }
638 
684  protected function ‪getPageAndRootline(ServerRequestInterface $request)
685  {
686  $requestedPageRowWithoutGroupCheck = [];
687  $this->page = $this->sys_page->getPage($this->id);
688  if (empty($this->page)) {
689  // If no page, we try to find the page above in the rootLine.
690  // Page is 'not found' in case the id itself was not an accessible page. code 1
691  $this->pageNotFound = 1;
692  $requestedPageIsHidden = false;
693  try {
694  $hiddenField = ‪$GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['disabled'] ?? '';
695  $includeHiddenPages = $this->context->getPropertyFromAspect('visibility', 'includeHiddenPages') || $this->context->getPropertyFromAspect('backend.user', 'isLoggedIn', false);
696  if (!empty($hiddenField) && !$includeHiddenPages) {
697  // Page is "hidden" => 404 (deliberately done in default language, as this cascades to language overlays)
698  $rawPageRecord = $this->sys_page->getPage_noCheck($this->id);
699 
700  // If page record could not be resolved throw exception
701  if ($rawPageRecord === []) {
702  $message = 'The requested page does not exist!';
703  try {
704  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
705  $request,
706  $message,
708  );
709  throw new PropagateResponseException($response, 1674144383);
710  } catch (PageNotFoundException $e) {
711  throw new PageNotFoundException($message, 1674539331);
712  }
713  }
714 
715  $requestedPageIsHidden = (bool)$rawPageRecord[$hiddenField];
716  }
717 
718  $requestedPageRowWithoutGroupCheck = $this->sys_page->getPage($this->id, true);
719  if (!empty($requestedPageRowWithoutGroupCheck)) {
720  $this->pageAccessFailureHistory['direct_access'][] = $requestedPageRowWithoutGroupCheck;
721  }
722  $this->rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $this->id, $this->MP, $this->context)->get();
723  if (!empty($this->rootLine)) {
724  $c = count($this->rootLine) - 1;
725  while ($c > 0) {
726  // Add to page access failure history:
727  $this->pageAccessFailureHistory['direct_access'][] = $this->rootLine[$c];
728  // Decrease to next page in rootline and check the access to that, if OK, set as page record and ID value.
729  $c--;
730  $this->id = (int)$this->rootLine[$c]['uid'];
731  $this->page = $this->sys_page->getPage($this->id);
732  if (!empty($this->page)) {
733  break;
734  }
735  }
736  }
737  } catch (RootLineException $e) {
738  $this->rootLine = [];
739  }
740  // If still no page...
741  if ($requestedPageIsHidden || (empty($requestedPageRowWithoutGroupCheck) && empty($this->page))) {
742  $message = 'The requested page does not exist!';
743  try {
744  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
745  $request,
746  $message,
748  );
749  throw new PropagateResponseException($response, 1533931330);
750  } catch (PageNotFoundException $e) {
751  throw new PageNotFoundException($message, 1301648780);
752  }
753  }
754  }
755  // Spacer and sysfolders is not accessible in frontend
756  $pageDoktype = (int)($this->page['doktype'] ?? 0);
757  $isSpacerOrSysfolder = $pageDoktype === ‪PageRepository::DOKTYPE_SPACER || $pageDoktype === ‪PageRepository::DOKTYPE_SYSFOLDER;
758  // Page itself is not accessible, but the parent page is a spacer/sysfolder
759  if ($isSpacerOrSysfolder && !empty($requestedPageRowWithoutGroupCheck)) {
760  try {
761  $response = GeneralUtility::makeInstance(ErrorController::class)->accessDeniedAction(
762  $request,
763  'Subsection was found and not accessible',
765  );
766  throw new PropagateResponseException($response, 1633171038);
767  } catch (PageNotFoundException $e) {
768  throw new PageNotFoundException('Subsection was found and not accessible', 1633171172);
769  }
770  }
771 
772  if ($isSpacerOrSysfolder) {
773  $message = 'The requested page does not exist!';
774  try {
775  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
776  $request,
777  $message,
779  );
780  throw new PropagateResponseException($response, 1533931343);
781  } catch (PageNotFoundException $e) {
782  throw new PageNotFoundException($message, 1301648781);
783  }
784  }
785  // Is the ID a link to another page??
786  if ($pageDoktype === ‪PageRepository::DOKTYPE_SHORTCUT) {
787  // We need to clear MP if the page is a shortcut. Reason is if the shortcut goes to another page, then we LEAVE the rootline which the MP expects.
788  $this->MP = '';
789  // saving the page so that we can check later - when we know
790  // about languages - whether we took the correct shortcut or
791  // whether a translation of the page overwrites the shortcut
792  // target and we need to follow the new target
793  $this->‪settingLanguage($request);
794  $this->originalShortcutPage = ‪$this->page;
795  $this->page = $this->sys_page->resolveShortcutPage($this->page, true);
796  $this->id = (int)$this->page['uid'];
797  $pageDoktype = (int)($this->page['doktype'] ?? 0);
798  }
799  // If the page is a mountpoint which should be overlaid with the contents of the mounted page,
800  // it must never be accessible directly, but only in the mountpoint context. Therefore we change
801  // the current ID and the user is redirected by checkPageForMountpointRedirect().
802  if ($pageDoktype === ‪PageRepository::DOKTYPE_MOUNTPOINT && $this->page['mount_pid_ol']) {
803  $this->originalMountPointPage = ‪$this->page;
804  $this->page = $this->sys_page->getPage($this->page['mount_pid']);
805  if (empty($this->page)) {
806  $message = 'This page (ID ' . $this->originalMountPointPage['uid'] . ') is of type "Mount point" and '
807  . 'mounts a page which is not accessible (ID ' . $this->originalMountPointPage['mount_pid'] . ').';
808  throw new PageNotFoundException($message, 1402043263);
809  }
810  // If the current page is a shortcut, the MP parameter will be replaced
811  if ($this->MP === '' || !empty($this->originalShortcutPage)) {
812  $this->MP = $this->page['uid'] . '-' . $this->originalMountPointPage['uid'];
813  } else {
814  $this->MP .= ',' . $this->page['uid'] . '-' . $this->originalMountPointPage['uid'];
815  }
816  $this->id = (int)$this->page['uid'];
817  $pageDoktype = (int)($this->page['doktype'] ?? 0);
818  }
819  // Gets the rootLine
820  try {
821  $this->rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $this->id, $this->MP, $this->context)->get();
822  } catch (RootLineException $e) {
823  $this->rootLine = [];
824  }
825  // If not rootline we're off...
826  if (empty($this->rootLine)) {
827  $message = 'The requested page didn\'t have a proper connection to the tree-root!';
828  $this->‪logPageAccessFailure($message, $request);
829  try {
830  $response = GeneralUtility::makeInstance(ErrorController::class)->internalErrorAction(
831  $request,
832  $message,
834  );
835  throw new PropagateResponseException($response, 1533931350);
836  } catch (AbstractServerErrorException $e) {
837  $this->logger->error($message, ['exception' => $e]);
838  $exceptionClass = get_class($e);
839  throw new $exceptionClass($message, 1301648167);
840  }
841  }
842  // Checking for include section regarding the hidden/starttime/endtime/fe_user (that is access control of a whole subbranch!)
843  if ($this->‪checkRootlineForIncludeSection()) {
844  if (empty($this->rootLine)) {
845  $message = 'The requested page does not exist!';
846  try {
847  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
848  $request,
849  $message,
851  );
852  throw new PropagateResponseException($response, 1533931351);
853  } catch (AbstractServerErrorException $e) {
854  $this->logger->warning($message);
855  $exceptionClass = get_class($e);
856  throw new $exceptionClass($message, 1301648234);
857  }
858  } else {
859  $el = reset($this->rootLine);
860  $this->id = (int)$el['uid'];
861  $this->page = $this->sys_page->getPage($this->id);
862  try {
863  $this->rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $this->id, $this->MP, $this->context)->get();
864  } catch (RootLineException $e) {
865  $this->rootLine = [];
866  }
867  }
868  }
869  }
870 
893  protected function ‪checkRootlineForIncludeSection(): bool
894  {
895  $c = count($this->rootLine);
896  $removeTheRestFlag = false;
897  $accessVoter = GeneralUtility::makeInstance(RecordAccessVoter::class);
898  for ($a = 0; $a < $c; $a++) {
899  if (!$accessVoter->accessGrantedForPageInRootLine($this->rootLine[$a], $this->context)) {
900  // Add to page access failure history and mark the page as not found
901  // Keep the rootline however to trigger an access denied error instead of a service unavailable error
902  $this->pageAccessFailureHistory['sub_section'][] = $this->rootLine[$a];
903  $this->pageNotFound = 2;
904  }
905 
906  if ((int)$this->rootLine[$a]['doktype'] === ‪PageRepository::DOKTYPE_BE_USER_SECTION) {
907  // If there is a backend user logged in, check if they have read access to the page:
908  if ($this->context->getPropertyFromAspect('backend.user', 'isLoggedIn', false)) {
909  // If there was no page selected, the user apparently did not have read access to the
910  // current page (not position in rootline) and we set the remove-flag...
911  if (!$this->‪getBackendUser()->doesUserHaveAccess($this->page, ‪Permission::PAGE_SHOW)) {
912  $removeTheRestFlag = true;
913  }
914  } else {
915  // Don't go here, if there is no backend user logged in.
916  $removeTheRestFlag = true;
917  }
918  }
919  if ($removeTheRestFlag) {
920  // Page is 'not found' in case a subsection was found and not accessible, code 2
921  $this->pageNotFound = 2;
922  unset($this->rootLine[$a]);
923  }
924  }
925  return $removeTheRestFlag;
926  }
927 
934  public function ‪getPageAccessFailureReasons(string $failureReasonCode = null)
935  {
936  ‪$output = [];
937  if ($failureReasonCode) {
938  ‪$output['code'] = $failureReasonCode;
939  }
940  $combinedRecords = array_merge(
941  is_array($this->pageAccessFailureHistory['direct_access'] ?? false) ? $this->pageAccessFailureHistory['direct_access'] : [['fe_group' => 0]],
942  is_array($this->pageAccessFailureHistory['sub_section'] ?? false) ? $this->pageAccessFailureHistory['sub_section'] : []
943  );
944  if (!empty($combinedRecords)) {
945  $accessVoter = GeneralUtility::makeInstance(RecordAccessVoter::class);
946  foreach ($combinedRecords as $k => $pagerec) {
947  // If $k=0 then it is the very first page the original ID was pointing at and that will get a full check of course
948  // If $k>0 it is parent pages being tested. They are only significant for the access to the first page IF they had the extendToSubpages flag set, hence checked only then!
949  if (!$k || $pagerec['extendToSubpages']) {
950  if ($pagerec['hidden'] ?? false) {
951  ‪$output['hidden'][$pagerec['uid']] = true;
952  }
953  if (isset($pagerec['starttime']) && $pagerec['starttime'] > ‪$GLOBALS['SIM_ACCESS_TIME']) {
954  ‪$output['starttime'][$pagerec['uid']] = $pagerec['starttime'];
955  }
956  if (isset($pagerec['endtime']) && $pagerec['endtime'] != 0 && $pagerec['endtime'] <= ‪$GLOBALS['SIM_ACCESS_TIME']) {
957  ‪$output['endtime'][$pagerec['uid']] = $pagerec['endtime'];
958  }
959  if (!$accessVoter->groupAccessGranted('pages', $pagerec, $this->context)) {
960  ‪$output['fe_group'][$pagerec['uid']] = $pagerec['fe_group'];
961  }
962  }
963  }
964  }
965  return ‪$output;
966  }
967 
968  /********************************************
969  *
970  * Template and caching related functions.
971  *
972  *******************************************/
973 
974  protected function ‪setPageArguments(PageArguments ‪$pageArguments): void
975  {
976  $this->pageArguments = ‪$pageArguments;
977  $this->id = $pageArguments->‪getPageId();
978  // We store the originally requested id
979  $this->requestedId = ‪$this->id;
980  $this->type = (int)(‪$pageArguments->‪getPageType() ?: 0);
981  if (‪$GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
982  $this->MP = (string)(‪$pageArguments->getArguments()['MP'] ?? '');
983  // Ensure no additional arguments are given via the &MP=123-345,908-172 (e.g. "/")
984  $this->MP = preg_replace('/[^0-9,-]/', '', $this->MP);
985  }
986  }
987 
992  protected function ‪getRelevantParametersForCachingFromPageArguments(PageArguments ‪$pageArguments): array
993  {
994  $queryParams = ‪$pageArguments->getDynamicArguments();
995  if (!empty($queryParams) && (‪$pageArguments->getArguments()['cHash'] ?? false)) {
996  $queryParams['id'] = ‪$pageArguments->‪getPageId();
997  return GeneralUtility::makeInstance(CacheHashCalculator::class)
998  ->getRelevantParameters(‪HttpUtility::buildQueryString($queryParams));
999  }
1000  return [];
1001  }
1002 
1021  public function ‪getFromCache(ServerRequestInterface $request): ServerRequestInterface
1022  {
1023  // Reset some state.
1024  // @todo: Find out which resets are really needed here - Since this is called from a
1025  // relatively early middleware, we can expect these properties to be not set already?!
1026  $this->content = '';
1027  $this->config = [];
1028  $this->pageContentWasLoadedFromCache = false;
1029 
1030  // Very first thing, *always* executed: TypoScript is one factor that influences page content.
1031  // There can be multiple cache entries per page, when TypoScript conditions on the same page
1032  // create different TypoScript. We thus need the sys_template rows relevant for this page.
1033  // @todo: Even though all rootline sys_template records are fetched with only one query
1034  // in below implementation, we could potentially join or sub select sys_template
1035  // records already when pages rootline is queried. This will save one query
1036  // and needs an implementation in getPageAndRootline() which is called via determineId()
1037  // in TypoScriptFrontendInitialization. This could be done when getPageAndRootline()
1038  // switches to a CTE query instead of using RootlineUtility.
1039  $sysTemplateRepository = GeneralUtility::makeInstance(SysTemplateRepository::class);
1040  $sysTemplateRows = $sysTemplateRepository->getSysTemplateRowsByRootline($this->rootLine, $request);
1041  // Needed for cache calculations. Put into a variable here to not serialize multiple times.
1042  $serializedSysTemplateRows = serialize($sysTemplateRows);
1043 
1044  // Early exception if there is no sys_template at all.
1045  if (empty($sysTemplateRows)) {
1046  $message = 'No TypoScript record found!';
1047  $this->logger->alert($message);
1048  try {
1049  $response = GeneralUtility::makeInstance(ErrorController::class)->internalErrorAction(
1050  $request,
1051  $message,
1053  );
1054  throw new PropagateResponseException($response, 1533931380);
1055  } catch (AbstractServerErrorException $e) {
1056  $exceptionClass = get_class($e);
1057  throw new $exceptionClass($message, 1294587218);
1058  }
1059  }
1060 
1061  // Calculate "local" rootLine that stops at first root=1 template, will be set as $this->config['rootLine']
1062  $sysTemplateRowsIndexedByPid = array_combine(array_column($sysTemplateRows, 'pid'), $sysTemplateRows);
1063  $localRootline = [];
1064  foreach ($this->rootLine as $rootlinePage) {
1065  array_unshift($localRootline, $rootlinePage);
1066  if ((int)($rootlinePage['uid'] ?? 0) > 0
1067  && (int)($sysTemplateRowsIndexedByPid[$rootlinePage['uid']]['root'] ?? 0) === 1
1068  ) {
1069  break;
1070  }
1071  }
1072 
1073  ‪$site = $this->‪getSite();
1074 
1075  $tokenizer = new LossyTokenizer();
1076  $treeBuilder = GeneralUtility::makeInstance(SysTemplateTreeBuilder::class);
1077  $includeTreeTraverser = new IncludeTreeTraverser();
1078  $includeTreeTraverserConditionVerdictAware = new ConditionVerdictAwareIncludeTreeTraverser();
1079  $cacheManager = GeneralUtility::makeInstance(CacheManager::class);
1081  $typoscriptCache = null;
1082  if (!$this->no_cache) {
1083  // $this->no_cache = true might have been set by earlier TypoScriptFrontendInitialization middleware.
1084  // This means we don't do fancy cache stuff, calculate full TypoScript and ignore page cache.
1086  $typoscriptCache = $cacheManager->getCache('typoscript');
1087  }
1088 
1089  $topDownRootLine = ‪$this->rootLine;
1090  ksort($topDownRootLine);
1091  $expressionMatcherVariables = [
1092  'request' => $request,
1093  'pageId' => ‪$this->id,
1094  // @todo We're using the full page row here to provide all necessary fields (e.g. "backend_layout"),
1095  // which are currently not included in the rows, RootlineUtility provides by default. We might
1096  // want to switch to $this->rootline as soon as it contains all fields.
1097  'page' => ‪$this->page,
1098  'fullRootLine' => $topDownRootLine,
1099  'localRootLine' => $localRootline,
1100  'site' => ‪$site,
1101  'siteLanguage' => $request->‪getAttribute('language'),
1102  'tsfe' => $this,
1103  ];
1104 
1105  // We *always* need the TypoScript constants, one way or the other: Setup conditions can use constants,
1106  // so we need the constants to substitute their values within setup conditions.
1107  $constantConditionIncludeListCacheIdentifier = 'constant-condition-include-list-' . sha1($serializedSysTemplateRows);
1108  $constantConditionList = [];
1109  $constantsAst = new RootNode();
1110  $flatConstants = [];
1111  $serializedConstantConditionList = '';
1112  $gotConstantFromCache = false;
1113  if (!$this->no_cache && $constantConditionIncludeTree = $typoscriptCache->require($constantConditionIncludeListCacheIdentifier)) {
1114  // We got the flat list of all constants conditions for this TypoScript combination from cache. Good. We traverse
1115  // this list to calculate "current" condition verdicts. With a hash of this list together with a hash of the
1116  // TypoScript sys_templates, we try to retrieve the full constants TypoScript from cache.
1117  $conditionMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
1118  $conditionMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
1119  // It does not matter if we use IncludeTreeTraverser or ConditionVerdictAwareIncludeTreeTraverser here:
1120  // Condition list is flat, not nested. IncludeTreeTraverser has an if() less, so we use that one.
1121  $includeTreeTraverser->traverse($constantConditionIncludeTree, [$conditionMatcherVisitor]);
1122  $constantConditionList = $conditionMatcherVisitor->getConditionListWithVerdicts();
1123  // Needed for cache identifier calculations. Put into a variable here to not serialize multiple times.
1124  $serializedConstantConditionList = serialize($constantConditionList);
1125  $constantCacheEntryIdentifier = 'constant-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList);
1126  $constantsCacheEntry = $typoscriptCache->require($constantCacheEntryIdentifier);
1127  if (is_array($constantsCacheEntry)) {
1128  $constantsAst = $constantsCacheEntry['ast'];
1129  $flatConstants = $constantsCacheEntry['flatConstants'];
1130  $gotConstantFromCache = true;
1131  }
1132  }
1133  if ($this->no_cache || !$gotConstantFromCache) {
1134  // We did not get constants from cache, or are not allowed to use cache. We have to build constants from scratch.
1135  // This means we'll fetch the full constants include tree (from cache if possible), register the condition
1136  // matcher and register the AST builder and traverse include tree to retrieve constants AST and calculate
1137  // 'flat constants' from it. Both are cached if allowed afterwards for the 'if' above to kick in next time.
1138  if ($this->no_cache) {
1139  // Note $typoscriptCache *is not* hand over here: IncludeTree is calculated from scratch, we're not allowed to use cache.
1140  $constantIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('constants', $sysTemplateRows, $tokenizer, ‪$site);
1141  } else {
1142  // Note $typoscriptCache *is* hand over here, we can potentially grab the fully cached includeTree here, or cache entry will be created.
1143  $constantIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('constants', $sysTemplateRows, $tokenizer, ‪$site, $typoscriptCache);
1144  }
1145  $conditionMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
1146  $conditionMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
1147  $includeTreeTraverserConditionVerdictAwareVisitors = [];
1148  $includeTreeTraverserConditionVerdictAwareVisitors[] = $conditionMatcherVisitor;
1149  $constantAstBuilderVisitor = GeneralUtility::makeInstance(IncludeTreeAstBuilderVisitor::class);
1150  $includeTreeTraverserConditionVerdictAwareVisitors[] = $constantAstBuilderVisitor;
1151  // We must use ConditionVerdictAwareIncludeTreeTraverser here: This one does not walk into
1152  // children for not matching conditions, which is important to create the correct AST.
1153  $includeTreeTraverserConditionVerdictAware->traverse($constantIncludeTree, $includeTreeTraverserConditionVerdictAwareVisitors);
1154  $constantsAst = $constantAstBuilderVisitor->getAst();
1155  $flatConstants = $constantsAst->flatten();
1156  if (!$this->no_cache) {
1157  // We are allowed to cache and can create both the full list of conditions, plus the constant AST and flat constant
1158  // list cache entry. To do that, we need all (!) conditions, but the above ConditionVerdictAwareIncludeTreeTraverser
1159  // did not find nested conditions if an upper condition did not match. We thus have to traverse include tree a
1160  // second time with the IncludeTreeTraverser that does traverse into not matching conditions as well.
1161  $includeTreeTraverserVisitors = [];
1162  $conditionMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
1163  $conditionMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
1164  $includeTreeTraverserVisitors[] = $constantAstBuilderVisitor;
1165  $constantConditionIncludeListAccumulatorVisitor = new IncludeTreeConditionIncludeListAccumulatorVisitor();
1166  $includeTreeTraverserVisitors[] = $constantConditionIncludeListAccumulatorVisitor;
1167  $includeTreeTraverser->traverse($constantIncludeTree, $includeTreeTraverserVisitors);
1168  $constantConditionList = $conditionMatcherVisitor->getConditionListWithVerdicts();
1169  // Needed for cache identifier calculations. Put into a variable here to not serialize multiple times.
1170  $serializedConstantConditionList = serialize($constantConditionList);
1171  $typoscriptCache->set($constantConditionIncludeListCacheIdentifier, 'return unserialize(\'' . addcslashes(serialize($constantConditionIncludeListAccumulatorVisitor->getConditionIncludes()), '\'\\') . '\');');
1172  $constantCacheEntryIdentifier = 'constant-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList);
1173  $typoscriptCache->set($constantCacheEntryIdentifier, 'return unserialize(\'' . addcslashes(serialize(['ast' => $constantsAst, 'flatConstants' => $flatConstants]), '\'\\') . '\');');
1174  }
1175  }
1176 
1177  $frontendTypoScript = new FrontendTypoScript($constantsAst, $flatConstants);
1178 
1179  // Next step: We have constants and fetch the setup include tree now. We then calculate setup condition verdicts
1180  // and set the constants to allow substitution of constants within conditions. Next, we traverse include tree
1181  // to calculate conditions verdicts and gather them along the way. A hash of these conditions with their verdicts
1182  // is then part of the page cache identifier hash: When a condition on a page creates a different result, the hash
1183  // is different from an existing page cache entry and a new one is created later.
1184  $setupConditionIncludeListCacheIdentifier = 'setup-condition-include-list-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList);
1185  $setupConditionList = [];
1186  $gotSetupConditionsFromCache = false;
1187  if (!$this->no_cache && $setupConditionIncludeTree = $typoscriptCache->require($setupConditionIncludeListCacheIdentifier)) {
1188  // We got the flat list of all setup conditions for this TypoScript combination from cache. Good. We traverse
1189  // this list to calculate "current" condition verdicts, which we need as hash to be part of page cache identifier.
1190  $includeTreeTraverserVisitors = [];
1191  $setupConditionConstantSubstitutionVisitor = new IncludeTreeSetupConditionConstantSubstitutionVisitor();
1192  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flatConstants);
1193  $includeTreeTraverserVisitors[] = $setupConditionConstantSubstitutionVisitor;
1194  $setupMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
1195  $setupMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
1196  $includeTreeTraverserVisitors[] = $setupMatcherVisitor;
1197  // It does not matter if we use IncludeTreeTraverser or ConditionVerdictAwareIncludeTreeTraverser here:
1198  // Condition list is flat, not nested. IncludeTreeTraverser has an if() less, so we use that one.
1199  $includeTreeTraverser->traverse($setupConditionIncludeTree, $includeTreeTraverserVisitors);
1200  $setupConditionList = $setupMatcherVisitor->getConditionListWithVerdicts();
1201  $gotSetupConditionsFromCache = true;
1202  }
1203  if ($this->no_cache || !$gotSetupConditionsFromCache) {
1204  // We did not get setup condition list from cache, or are not allowed to use cache. We have to build setup
1205  // condition list from scratch. This means we'll fetch the full setup include tree (from cache if possible),
1206  // register the constant substitution visitor, and register condition matcher and register the condition
1207  // accumulator visitor.
1208  if ($this->no_cache) {
1209  // Note $typoscriptCache *is not* hand over here: IncludeTree is calculated from scratch, we're not allowed to use cache.
1210  $setupIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('setup', $sysTemplateRows, $tokenizer, ‪$site);
1211  } else {
1212  // Note $typoscriptCache *is* hand over here, we can potentially grab the fully cached includeTree here, or cache entry will be created.
1213  $setupIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('setup', $sysTemplateRows, $tokenizer, ‪$site, $typoscriptCache);
1214  }
1215  $includeTreeTraverserVisitors = [];
1216  $setupConditionConstantSubstitutionVisitor = new IncludeTreeSetupConditionConstantSubstitutionVisitor();
1217  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flatConstants);
1218  $includeTreeTraverserVisitors[] = $setupConditionConstantSubstitutionVisitor;
1219  $setupMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
1220  $setupMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
1221  $includeTreeTraverserVisitors[] = $setupMatcherVisitor;
1222  $setupConditionIncludeListAccumulatorVisitor = new IncludeTreeConditionIncludeListAccumulatorVisitor();
1223  $includeTreeTraverserVisitors[] = $setupConditionIncludeListAccumulatorVisitor;
1224  // It is important we use IncludeTreeTraverser here: We to have the condition verdicts of *all* conditions, plus
1225  // want to accumulate all of them. The ConditionVerdictAwareIncludeTreeTraverser wouldn't walk into nested
1226  // conditions if an upper one does not match.
1227  $includeTreeTraverser->traverse($setupIncludeTree, $includeTreeTraverserVisitors);
1228  $setupConditionList = $setupMatcherVisitor->getConditionListWithVerdicts();
1229  if (!$this->no_cache) {
1230  $typoscriptCache->set($setupConditionIncludeListCacheIdentifier, 'return unserialize(\'' . addcslashes(serialize($setupConditionIncludeListAccumulatorVisitor->getConditionIncludes()), '\'\\') . '\');');
1231  }
1232  }
1233 
1234  // We now gathered everything to calculate the page cache identifier: It depends on sys_template rows, the calculated
1235  // constant condition verdicts, the setup condition verdicts, plus various not TypoScript related details like
1236  // obviously the page id.
1237  $this->lock = GeneralUtility::makeInstance(ResourceMutex::class);
1238  $this->newHash = $this->‪createHashBase($sysTemplateRows, $constantConditionList, $setupConditionList);
1239  if (!$this->no_cache) {
1240  if ($this->‪shouldAcquireCacheData($request)) {
1241  // Try to get a page cache row.
1242  $this->‪getTimeTracker()->‪push('Cache Row');
1243  $pageCacheRow = $this->pageCache->get($this->newHash);
1244  if (!is_array($pageCacheRow)) {
1245  // Nothing in the cache, we acquire an exclusive lock now.
1246  // There are two scenarios when locking: We're either the first process acquiring this lock. This means we'll
1247  // "immediately" get it and can continue with page rendering. Or, another process acquired the lock already. In
1248  // this case, the below call will wait until the lock is released again. The other process then probably wrote
1249  // a page cache entry, which we can use.
1250  // To handle the second case - if our process had to wait for another one creating the content for us - we
1251  // simply query the page cache again to see if there is a page cache now.
1252  $hadToWaitForLock = $this->lock->acquireLock('pages', $this->newHash);
1253  // From this point on we're the only one working on that page.
1254  if ($hadToWaitForLock) {
1255  // Query the cache again to see if the data is there meanwhile: We did not get the lock
1256  // immediately, chances are high the other process created a page cache for us.
1257  // There is a small chance the other process actually pageCache->set() the content,
1258  // but pageCache->get() still returns false, for instance when a database returned "done"
1259  // for the INSERT, but SELECT still does not return the new row - may happen in multi-head
1260  // DB instances, and with some other distributed cache backends as well. The worst that
1261  // can happen here is the page generation is done too often, which we accept as trade-off.
1262  $pageCacheRow = $this->pageCache->get($this->newHash);
1263  if (is_array($pageCacheRow)) {
1264  // We have the content, some other process did the work for us, release our lock again.
1265  $this->‪releaseLocks();
1266  }
1267  }
1268  // We keep the lock set, because we are the ones generating the page now and filling the cache.
1269  // This indicates that we have to release the lock later in releaseLocks()!
1270  }
1271  if (is_array($pageCacheRow)) {
1272  // Note this especially populates $this->config!
1273  $this->‪populatePageDataFromCache($pageCacheRow);
1274  }
1275  $this->‪getTimeTracker()->‪pull();
1276  } else {
1277  // User forced page cache rebuilding. Get a lock for the page content so other processes can't interfere.
1278  $this->lock->acquireLock('pages', $this->newHash);
1279  }
1280  } else {
1281  // Caching is not allowed. We'll rebuild the page. Lock this.
1282  $this->lock->acquireLock('pages', $this->newHash);
1283  }
1284 
1285  $forceTemplateParsing = $this->context->getPropertyFromAspect('typoscript', 'forcedTemplateParsing');
1286  if ($this->no_cache || empty($this->config) || $this->‪isINTincScript() || $forceTemplateParsing) {
1287  // We don't need the full setup AST in many cached scenarios. However, if no_cache is set, if no page cache
1288  // entry could be loaded, if the page cache entry has _INT object, or if the user forced template
1289  // parsing (adminpanel), then we still need the full setup AST. If there is "just" an _INT object, we can
1290  // use a possible cache entry for the setup AST, which speeds up _INT parsing quite a bit. In other cases
1291  // we calculate full setup AST and cache it if allowed.
1292  $setupTypoScriptCacheIdentifier = 'setup-' . sha1($serializedSysTemplateRows . $serializedConstantConditionList . serialize($setupConditionList));
1293  $gotSetupFromCache = false;
1294  $setupArray = [];
1295  if (!$this->no_cache && !$forceTemplateParsing) {
1296  // We need AST, but we are allowed to potentially get it from cache.
1297  if ($setupTypoScriptCache = $typoscriptCache->require($setupTypoScriptCacheIdentifier)) {
1298  $frontendTypoScript->setSetupTree($setupTypoScriptCache['ast']);
1299  $setupArray = $setupTypoScriptCache['array'];
1300  $gotSetupFromCache = true;
1301  }
1302  }
1303  if ($this->no_cache || $forceTemplateParsing || !$gotSetupFromCache) {
1304  // We need AST and couldn't get it from cache or are now allowed to. We thus need the full setup
1305  // IncludeTree, which we can get from cache again if allowed, or is calculated a-new if not.
1306  if ($this->no_cache || $forceTemplateParsing) {
1307  // Note $typoscriptCache *is not* hand over here: IncludeTree is calculated from scratch, we're not allowed to use cache.
1308  $setupIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('setup', $sysTemplateRows, $tokenizer, ‪$site);
1309  } else {
1310  // Note $typoscriptCache *is* hand over here, we can potentially grab the fully cached includeTree here, or cache entry will be created.
1311  $setupIncludeTree = $treeBuilder->getTreeBySysTemplateRowsAndSite('setup', $sysTemplateRows, $tokenizer, ‪$site, $typoscriptCache);
1312  }
1313  $includeTreeTraverserConditionVerdictAwareVisitors = [];
1314  $setupConditionConstantSubstitutionVisitor = new IncludeTreeSetupConditionConstantSubstitutionVisitor();
1315  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flatConstants);
1316  $includeTreeTraverserConditionVerdictAwareVisitors[] = $setupConditionConstantSubstitutionVisitor;
1317  $setupMatcherVisitor = GeneralUtility::makeInstance(IncludeTreeConditionMatcherVisitor::class);
1318  $setupMatcherVisitor->initializeExpressionMatcherWithVariables($expressionMatcherVariables);
1319  $includeTreeTraverserConditionVerdictAwareVisitors[] = $setupMatcherVisitor;
1320  $setupAstBuilderVisitor = GeneralUtility::makeInstance(IncludeTreeAstBuilderVisitor::class);
1321  $setupAstBuilderVisitor->setFlatConstants($flatConstants);
1322  $includeTreeTraverserConditionVerdictAwareVisitors[] = $setupAstBuilderVisitor;
1323  $includeTreeTraverserConditionVerdictAware->traverse($setupIncludeTree, $includeTreeTraverserConditionVerdictAwareVisitors);
1324  $setupAst = $setupAstBuilderVisitor->getAst();
1325  $frontendTypoScript->setSetupTree($setupAst);
1326 
1327  // Create top-level setup AST 'types' node from all top-level PAGE objects.
1328  // This is essentially a preparation for type-lookup below and should vanish later.
1329  $typesNode = new ChildNode('types');
1330  $gotTypeNumZero = false;
1331  foreach ($setupAst->getNextChild() as $setupChild) {
1332  if ($setupChild->getValue() === 'PAGE') {
1333  $typeNumChild = $setupChild->getChildByName('typeNum');
1334  if ($typeNumChild) {
1335  $typeNumValue = $typeNumChild->getValue();
1336  $typesSubNode = new ChildNode($typeNumValue);
1337  $typesSubNode->setValue($setupChild->getName());
1338  $typesNode->addChild($typesSubNode);
1339  if ($typeNumValue === '0') {
1340  $gotTypeNumZero = true;
1341  }
1342  } elseif (!$gotTypeNumZero) {
1343  // The first PAGE node that has no typeNum = 0 is considered '0' automatically.
1344  $typesSubNode = new ChildNode('0');
1345  $typesSubNode->setValue($setupChild->getName());
1346  $typesNode->addChild($typesSubNode);
1347  $gotTypeNumZero = true;
1348  }
1349  }
1350  }
1351  if ($typesNode->hasChildren()) {
1352  $setupAst->addChild($typesNode);
1353  }
1354  $setupArray = $setupAst->toArray();
1355  if (!$this->no_cache && !$forceTemplateParsing) {
1356  // Write cache entry for AST and its array representation, we're allowed to do it.
1357  $typoscriptCache->set($setupTypoScriptCacheIdentifier, 'return unserialize(\'' . addcslashes(serialize(['ast' => $setupAst, 'array' => $setupArray]), '\'\\') . '\');');
1358  }
1359  }
1360 
1361  $typoScriptPageTypeName = $setupArray['types.'][‪$this->type] ?? '';
1362  $this->pSetup = $setupArray[$typoScriptPageTypeName . '.'] ?? '';
1363 
1364  if (!is_array($this->pSetup)) {
1365  $this->logger->alert('The page is not configured! [type={type}][{type_name}].', ['type' => $this->type, 'type_name' => $typoScriptPageTypeName]);
1366  try {
1367  $message = 'The page is not configured! [type=' . $this->type . '][' . $typoScriptPageTypeName . '].';
1368  $response = GeneralUtility::makeInstance(ErrorController::class)->internalErrorAction(
1369  $request,
1370  $message,
1372  );
1373  throw new PropagateResponseException($response, 1533931374);
1374  } catch (AbstractServerErrorException $e) {
1375  $explanation = 'This means that there is no TypoScript object of type PAGE with typeNum=' . $this->type . ' configured.';
1376  $exceptionClass = get_class($e);
1377  throw new $exceptionClass($message . ' ' . $explanation, 1294587217);
1378  }
1379  }
1380 
1381  if (!isset($this->config['config'])) {
1382  $this->config['config'] = [];
1383  }
1384  // Filling the config-array, first with the main "config." part
1385  if (is_array($setupArray['config.'] ?? null)) {
1386  // @todo: These operations should happen on AST instead and array is exported (and cached) afterwards
1387  $setupArray['config.'] = array_replace_recursive($setupArray['config.'], $this->config['config']);
1388  $this->config['config'] = $setupArray['config.'];
1389  }
1390  // Override it with the page/type-specific "config."
1391  if (is_array($this->pSetup['config.'] ?? null)) {
1392  $this->config['config'] = array_replace_recursive($this->config['config'], $this->pSetup['config.']);
1393  }
1394  $this->config['rootLine'] = $localRootline;
1395  $frontendTypoScript->setSetupArray($setupArray);
1396  }
1397 
1398  // Set $this->no_cache TRUE if the config.no_cache value is set!
1399  if (!$this->no_cache && ($this->config['config']['no_cache'] ?? false)) {
1400  $this->‪set_no_cache('config.no_cache is set', true);
1401  }
1402 
1403  // Auto-configure settings when a site is configured
1404  $this->config['config']['absRefPrefix'] = $this->config['config']['absRefPrefix'] ?? 'auto';
1405 
1406  // Hook for postProcessing the configuration array
1407  $params = ['config' => &$this->config['config']];
1408  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'] ?? [] as $funcRef) {
1409  GeneralUtility::callUserFunction($funcRef, $params, $this);
1410  }
1411 
1412  return $request->withAttribute('frontend.typoscript', $frontendTypoScript);
1413  }
1414 
1425  protected function ‪populatePageDataFromCache(array $cachedData): void
1426  {
1427  // Call hook when a page is retrieved from cache
1428  $_params = ['pObj' => &$this, 'cache_pages_row' => &$cachedData];
1429  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'] ?? [] as $_funcRef) {
1430  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1431  }
1432  // Fetches the lowlevel config stored with the cached data
1433  $this->config = $cachedData['cache_data'];
1434  // Getting the content
1435  $this->content = $cachedData['content'];
1436  // Getting the content type
1437  $this->contentType = $cachedData['contentType'] ?? $this->contentType;
1438  // Setting flag, so we know, that some cached content has been loaded
1439  $this->pageContentWasLoadedFromCache = true;
1440  $this->cacheExpires = $cachedData['expires'];
1441  // Restore the current tags as they can be retrieved by getPageCacheTags()
1442  $this->pageCacheTags = $cachedData['cacheTags'] ?? [];
1443 
1444  if (isset($this->config['config']['debug'])) {
1445  $debugCacheTime = (bool)$this->config['config']['debug'];
1446  } else {
1447  $debugCacheTime = !empty(‪$GLOBALS['TYPO3_CONF_VARS']['FE']['debug']);
1448  }
1449  if ($debugCacheTime) {
1450  $dateFormat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
1451  $timeFormat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
1452  $this->debugInformationHeader = 'Cached page generated ' . date($dateFormat . ' ' . $timeFormat, $cachedData['tstamp'])
1453  . '. Expires ' . date($dateFormat . ' ' . $timeFormat, $cachedData['expires']);
1454  }
1455  }
1456 
1465  protected function ‪shouldAcquireCacheData(ServerRequestInterface $request): bool
1466  {
1467  // Trigger event for possible by-pass of requiring of page cache (for re-caching purposes)
1468  $event = new ShouldUseCachedPageDataIfAvailableEvent($request, $this, !$this->no_cache);
1469  GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch($event);
1470  return $event->shouldUseCachedPageData();
1471  }
1472 
1484  protected function ‪createHashBase(array $sysTemplateRows, array $constantConditionList, array $setupConditionList): string
1485  {
1486  // Fetch the list of user groups
1488  $userAspect = $this->context->getAspect('frontend.user');
1489  $hashParameters = [
1490  'id' => ‪$this->id,
1491  'type' => ‪$this->type,
1492  'groupIds' => (string)implode(',', $userAspect->getGroupIds()),
1493  'MP' => (string)‪$this->MP,
1494  'site' => $this->‪site->getIdentifier(),
1495  // Ensure the language base is used for the hash base calculation as well, otherwise TypoScript and page-related rendering
1496  // is not cached properly as we don't have any language-specific conditions anymore
1497  'siteBase' => (string)$this->‪language->getBase(),
1498  // additional variation trigger for static routes
1499  'staticRouteArguments' => $this->pageArguments->getStaticArguments(),
1500  // dynamic route arguments (if route was resolved)
1501  'dynamicArguments' => $this->‪getRelevantParametersForCachingFromPageArguments($this->pageArguments),
1502  'sysTemplateRows' => $sysTemplateRows,
1503  'constantConditionList' => $constantConditionList,
1504  'setupConditionList' => $setupConditionList,
1505  ];
1506  // Call hook to influence the hash calculation
1507  $_params = [
1508  'hashParameters' => &$hashParameters,
1509  ];
1510  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'] ?? [] as $_funcRef) {
1511  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1512  }
1513  return $this->id . '_' . sha1(serialize($hashParameters));
1514  }
1515 
1516  /********************************************
1517  *
1518  * Further initialization and data processing
1519  *
1520  *******************************************/
1527  protected function ‪settingLanguage(ServerRequestInterface $request)
1528  {
1529  // Get values from site language
1531 
1532  $languageId = $languageAspect->getId();
1533  $languageContentId = $languageAspect->getContentId();
1534 
1535  $pageTranslationVisibility = new ‪PageTranslationVisibility((int)($this->page['l18n_cfg'] ?? 0));
1536  // If the incoming language is set to another language than default
1537  if ($languageAspect->getId() > 0) {
1538  // Request the translation for the requested language
1539  $olRec = $this->sys_page->getPageOverlay($this->page, $languageAspect);
1540  $overlaidLanguageId = (int)($olRec['sys_language_uid'] ?? 0);
1541  if ($overlaidLanguageId !== $languageAspect->getId()) {
1542  // If requested translation is not available
1543  if ($pageTranslationVisibility->shouldHideTranslationIfNoTranslatedRecordExists()) {
1544  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
1545  $request,
1546  'Page is not available in the requested language.',
1548  );
1549  throw new ‪PropagateResponseException($response, 1533931388);
1550  }
1551  switch ($languageAspect->getLegacyLanguageMode()) {
1552  case 'strict':
1553  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
1554  $request,
1555  'Page is not available in the requested language (strict).',
1557  );
1558  throw new ‪PropagateResponseException($response, 1533931395);
1559  case 'content_fallback':
1560  // Setting content uid (but leaving the sys_language_uid) when a content_fallback
1561  // value was found.
1562  foreach ($languageAspect->getFallbackChain() as $orderValue) {
1563  if ($orderValue === '0' || $orderValue === 0 || $orderValue === '') {
1564  $languageContentId = 0;
1565  break;
1566  }
1567  if (‪MathUtility::canBeInterpretedAsInteger($orderValue) && $overlaidLanguageId === (int)$orderValue) {
1568  $languageContentId = (int)$orderValue;
1569  break;
1570  }
1571  if ($orderValue === 'pageNotFound') {
1572  // The existing fallbacks have not been found, but instead of continuing
1573  // page rendering with default language, a "page not found" message should be shown
1574  // instead.
1575  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
1576  $request,
1577  'Page is not available in the requested language (fallbacks did not apply).',
1579  );
1580  throw new ‪PropagateResponseException($response, 1533931402);
1581  }
1582  }
1583  break;
1584  default:
1585  // Default is that everything defaults to the default language...
1586  $languageId = ($languageContentId = 0);
1587  }
1588  }
1589 
1590  // Define the language aspect again now
1591  $languageAspect = GeneralUtility::makeInstance(
1592  LanguageAspect::class,
1593  $languageId,
1594  $languageContentId,
1595  $languageAspect->getOverlayType(),
1596  $languageAspect->getFallbackChain()
1597  );
1598 
1599  // Setting the $this->page if an overlay record was found (which it is only if a language is used)
1600  // Doing this ensures that page properties like the page title are resolved in the correct language
1601  $this->page = $olRec;
1602  }
1603 
1604  // Set the language aspect
1605  $this->context->setAspect('language', $languageAspect);
1606 
1607  // Setting sys_language_uid inside sys-page by creating a new page repository
1608  $this->sys_page = GeneralUtility::makeInstance(PageRepository::class, $this->context);
1609  // If default language is not available
1610  if ((!$languageAspect->getContentId() || !$languageAspect->getId())
1611  && $pageTranslationVisibility->shouldBeHiddenInDefaultLanguage()
1612  ) {
1613  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
1614  $request,
1615  'Page is not available in default language.',
1617  );
1618  throw new ‪PropagateResponseException($response, 1533931423);
1619  }
1620 
1621  if ($languageAspect->getId() > 0) {
1623  }
1624  }
1625 
1629  protected function ‪updateRootLinesWithTranslations()
1630  {
1631  try {
1632  $this->rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $this->id, $this->MP, $this->context)->get();
1633  } catch (‪RootLineException $e) {
1634  $this->rootLine = [];
1635  }
1636  }
1637 
1644  public function ‪calculateLinkVars(array $queryParams)
1645  {
1646  $this->linkVars = GeneralUtility::makeInstance(LinkVarsCalculator::class)
1647  ->getAllowedLinkVarsFromRequest(
1648  (string)($this->config['config']['linkVars'] ?? ''),
1649  $queryParams,
1650  $this->context
1651  );
1652  }
1653 
1662  public function ‪getRedirectUriForMountPoint(ServerRequestInterface $request): ?string
1663  {
1664  if (!empty($this->originalMountPointPage) && (int)$this->originalMountPointPage['doktype'] === ‪PageRepository::DOKTYPE_MOUNTPOINT) {
1665  return $this->‪getUriToCurrentPageForRedirect($request);
1666  }
1668  return null;
1669  }
1670 
1680  public function ‪getRedirectUriForShortcut(ServerRequestInterface $request): ?string
1681  {
1682  if (!empty($this->originalShortcutPage) && $this->originalShortcutPage['doktype'] == ‪PageRepository::DOKTYPE_SHORTCUT) {
1683  // Check if the shortcut page is actually on the current site, if not, this is a "page not found"
1684  // because the request was www.mydomain.com/?id=23 where page ID 23 (which is a shortcut) is on another domain/site.
1685  if ((int)($request->getQueryParams()['id'] ?? 0) > 0) {
1686  try {
1687  ‪$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($this->originalShortcutPage['l10n_parent'] ?: $this->originalShortcutPage['uid']);
1688  } catch (‪SiteNotFoundException $e) {
1689  ‪$site = null;
1690  }
1691  if (‪$site !== $this->‪site) {
1692  $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
1693  $request,
1694  'ID was outside the domain',
1696  );
1697  throw new ‪ImmediateResponseException($response, 1638022483);
1698  }
1699  }
1700  return $this->‪getUriToCurrentPageForRedirect($request);
1701  }
1702 
1703  return null;
1704  }
1705 
1709  protected function ‪getUriToCurrentPageForRedirect(ServerRequestInterface $request): string
1710  {
1711  $this->‪calculateLinkVars($request->getQueryParams());
1712  $parameter = $this->page['uid'];
1713  if ($this->type) {
1714  $parameter .= ',' . ‪$this->type;
1715  }
1716  return GeneralUtility::makeInstance(ContentObjectRenderer::class, $this)->createUrl([
1717  'parameter' => $parameter,
1718  'addQueryString' => 'untrusted',
1719  'addQueryString.' => ['exclude' => 'id,type'],
1720  'forceAbsoluteUrl' => true,
1721  ]);
1722  }
1723 
1724  /********************************************
1725  *
1726  * Page generation; cache handling
1727  *
1728  *******************************************/
1732  public function ‪isGeneratePage(): bool
1733  {
1734  return !$this->pageContentWasLoadedFromCache;
1735  }
1736 
1745  protected function ‪setPageCacheContent(string $content, array $data, int $expirationTstamp): array
1746  {
1747  $cacheData = [
1748  'page_id' => ‪$this->id,
1749  'content' => $content,
1750  'contentType' => $this->contentType,
1751  'cache_data' => $data,
1752  'expires' => $expirationTstamp,
1753  'tstamp' => ‪$GLOBALS['EXEC_TIME'],
1754  ];
1755  $this->cacheExpires = $expirationTstamp;
1756  $this->pageCacheTags[] = 'pageId_' . ‪$this->id;
1757  // Respect the page cache when content of pid is shown
1758  if ($this->id !== $this->contentPid) {
1759  $this->pageCacheTags[] = 'pageId_' . ‪$this->contentPid;
1760  }
1761  if (!empty($this->page['cache_tags'])) {
1762  $tags = GeneralUtility::trimExplode(',', $this->page['cache_tags'], true);
1763  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
1764  }
1765  $this->pageCacheTags = array_unique($this->pageCacheTags);
1766  // Add the cache themselves as well, because they are fetched by getPageCacheTags()
1767  $cacheData['cacheTags'] = $this->pageCacheTags;
1768  $this->pageCache->set($this->newHash, $cacheData, $this->pageCacheTags, $expirationTstamp - ‪$GLOBALS['EXEC_TIME']);
1769  return $cacheData;
1770  }
1771 
1777  public function ‪clearPageCacheContent()
1778  {
1779  $this->pageCache->remove($this->newHash);
1780  }
1781 
1792  protected function ‪setSysLastChanged()
1793  {
1794  // We only update the info if browsing the live workspace
1795  $isInWorkspace = $this->context->getPropertyFromAspect('workspace', 'isOffline', false);
1796  if ($isInWorkspace) {
1797  return;
1798  }
1799  if ($this->page['SYS_LASTCHANGED'] < (int)($this->register['SYS_LASTCHANGED'] ?? 0)) {
1800  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
1801  ->getConnectionForTable('pages');
1802  $pageId = $this->page['_PAGES_OVERLAY_UID'] ?? ‪$this->id;
1803  $connection->update(
1804  'pages',
1805  [
1806  'SYS_LASTCHANGED' => (int)$this->register['SYS_LASTCHANGED'],
1807  ],
1808  [
1809  'uid' => (int)$pageId,
1810  ]
1811  );
1812  }
1813  }
1814 
1822  protected function ‪setRegisterValueForSysLastChanged(array ‪$page): void
1823  {
1824  $this->register['SYS_LASTCHANGED'] = (int)‪$page['tstamp'];
1825  if ($this->register['SYS_LASTCHANGED'] < (int)$page['SYS_LASTCHANGED']) {
1826  $this->register['SYS_LASTCHANGED'] = (int)‪$page['SYS_LASTCHANGED'];
1827  }
1828  }
1829 
1836  public function ‪addCacheTags(array $tags)
1837  {
1838  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
1839  }
1840 
1841  public function ‪getPageCacheTags(): array
1842  {
1843  return $this->pageCacheTags;
1844  }
1845 
1846  /********************************************
1847  *
1848  * Page generation; rendering and inclusion
1849  *
1850  *******************************************/
1854  public function ‪generatePage_preProcessing()
1855  {
1856  // Used as a safety check in case a PHP script is falsely disabling $this->no_cache during page generation.
1857  $this->no_cacheBeforePageGen = ‪$this->no_cache;
1858  }
1859 
1870  protected function ‪resolveContentPid(ServerRequestInterface $request): int
1871  {
1872  if (!isset($this->page['content_from_pid']) || empty($this->page['content_from_pid'])) {
1874  }
1875  // make REAL copy of TSFE object - not reference!
1876  $temp_copy_TSFE = clone $this;
1877  // Set ->id to the content_from_pid value - we are going to evaluate this pid as was it a given id for a page-display!
1878  $temp_copy_TSFE->id = (int)$this->page['content_from_pid'];
1879  $temp_copy_TSFE->MP = '';
1880  $temp_copy_TSFE->getPageAndRootline($request);
1881  return $temp_copy_TSFE->id;
1882  }
1886  public function ‪preparePageContentGeneration(ServerRequestInterface $request)
1887  {
1888  $this->‪getTimeTracker()->‪push('Prepare page content generation');
1889  // calculate the absolute path prefix
1890  if (!empty($this->absRefPrefix = trim($this->config['config']['absRefPrefix'] ?? ''))) {
1891  if ($this->absRefPrefix === 'auto') {
1892  $normalizedParams = $request->getAttribute('normalizedParams');
1893  $this->absRefPrefix = $normalizedParams->getSitePath();
1894  }
1895  }
1896  // config.forceAbsoluteUrls will override absRefPrefix
1897  if ($this->config['config']['forceAbsoluteUrls'] ?? false) {
1898  $normalizedParams = $request->getAttribute('normalizedParams');
1899  $this->absRefPrefix = $normalizedParams->getSiteUrl();
1900  }
1901 
1902  // linkVars
1903  $this->‪calculateLinkVars($request->getQueryParams());
1904  // We need to set the doctype to "something defined" otherwise (because this method is called also during USER_INT renderings)
1905  $this->config['config']['doctype'] ??= 'html5';
1906  $docType = DocType::createFromConfigurationKey($this->config['config']['doctype']);
1907  $this->pageRenderer->setDocType($docType);
1908 
1909  // Global content object
1910  $this->‪newCObj($request);
1911  $this->‪getTimeTracker()->‪pull();
1912  }
1913 
1919  public function ‪generatePage_postProcessing(ServerRequestInterface $request)
1920  {
1921  $this->‪setAbsRefPrefix();
1922  // This is to ensure, that the page is NOT cached if the no_cache parameter was set before the page was generated.
1923  // This is a safety precaution, as it could have been unset by some script.
1924  if ($this->no_cacheBeforePageGen) {
1925  $this->‪set_no_cache('no_cache has been set before the page was generated - safety check', true);
1926  }
1927  $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
1928  $event = new AfterCacheableContentIsGeneratedEvent($request, $this, $this->newHash, !$this->no_cache);
1929  $event = $eventDispatcher->dispatch($event);
1930 
1931  // Processing if caching is enabled
1932  if ($event->isCachingEnabled()) {
1933  // Seconds until a cached page is too old
1934  $cacheTimeout = $this->‪get_cache_timeout();
1935  $timeOutTime = ‪$GLOBALS['EXEC_TIME'] + $cacheTimeout;
1936  // Write the page to cache
1937  $cachedInformation = $this->‪setPageCacheContent($this->content, $this->config, $timeOutTime);
1938 
1939  // Event for cache post processing (eg. writing static files)
1940  $event = new ‪AfterCachedPageIsPersistedEvent($request, $this, $this->newHash, $cachedInformation, $cacheTimeout);
1941  $eventDispatcher->dispatch($event);
1942  }
1943  $this->‪setSysLastChanged();
1944  }
1945 
1952  public function ‪generatePageTitle(): string
1953  {
1954  // Check for a custom pageTitleSeparator, and perform stdWrap on it
1955  $pageTitleSeparator = (string)$this->cObj->stdWrapValue('pageTitleSeparator', $this->config['config'] ?? []);
1956  if ($pageTitleSeparator !== '' && $pageTitleSeparator === ($this->config['config']['pageTitleSeparator'] ?? '')) {
1957  $pageTitleSeparator .= ' ';
1958  }
1959 
1960  $titleProvider = GeneralUtility::makeInstance(PageTitleProviderManager::class);
1961  if (!empty($this->config['config']['pageTitleCache'])) {
1962  $titleProvider->setPageTitleCache($this->config['config']['pageTitleCache']);
1963  }
1964  $pageTitle = $titleProvider->getTitle();
1965  $this->config['config']['pageTitleCache'] = $titleProvider->getPageTitleCache();
1966 
1967  $titleTagContent = $this->‪printTitle(
1968  $pageTitle,
1969  (bool)($this->config['config']['noPageTitle'] ?? false),
1970  (bool)($this->config['config']['pageTitleFirst'] ?? false),
1971  $pageTitleSeparator,
1972  (bool)($this->config['config']['showWebsiteTitle'] ?? true)
1973  );
1974  $this->config['config']['pageTitle'] = $titleTagContent;
1975  // stdWrap around the title tag
1976  $titleTagContent = $this->cObj->stdWrapValue('pageTitle', $this->config['config']);
1977 
1978  // config.noPageTitle = 2 - means do not render the page title
1979  if (isset($this->config['config']['noPageTitle']) && (int)$this->config['config']['noPageTitle'] === 2) {
1980  $titleTagContent = '';
1981  }
1982  if ($titleTagContent !== '') {
1983  $this->pageRenderer->setTitle($titleTagContent);
1984  }
1985  return (string)$titleTagContent;
1986  }
1987 
1999  protected function ‪printTitle(string $pageTitle, bool $noPageTitle = false, bool $showPageTitleFirst = false, string $pageTitleSeparator = '', bool $showWebsiteTitle = true): string
2000  {
2001  $websiteTitle = $showWebsiteTitle ? $this->‪getWebsiteTitle() : '';
2002  $pageTitle = $noPageTitle ? '' : $pageTitle;
2003  // only show a separator if there are both site title and page title
2004  if ($pageTitle === '' || $websiteTitle === '') {
2005  $pageTitleSeparator = '';
2006  } elseif (empty($pageTitleSeparator)) {
2007  // use the default separator if non given
2008  $pageTitleSeparator = ': ';
2009  }
2010  if ($showPageTitleFirst) {
2011  return $pageTitle . $pageTitleSeparator . $websiteTitle;
2012  }
2013  return $websiteTitle . $pageTitleSeparator . $pageTitle;
2014  }
2015 
2016  protected function ‪getWebsiteTitle(): string
2017  {
2018  if (trim($this->‪language->getWebsiteTitle()) !== '') {
2019  return trim($this->‪language->getWebsiteTitle());
2020  }
2021  if (trim($this->‪site->getConfiguration()['websiteTitle'] ?? '') !== '') {
2022  return trim($this->‪site->getConfiguration()['websiteTitle']);
2023  }
2024 
2025  return '';
2026  }
2027 
2031  public function ‪INTincScript(ServerRequestInterface $request): void
2032  {
2033  $this->additionalHeaderData = $this->config['INTincScript_ext']['additionalHeaderData'] ?? [];
2034  $this->additionalFooterData = $this->config['INTincScript_ext']['additionalFooterData'] ?? [];
2035  if (empty($this->config['INTincScript_ext']['pageRendererState'])) {
2036  $this->‪initPageRenderer();
2037  } else {
2038  $pageRendererState = unserialize($this->config['INTincScript_ext']['pageRendererState'], ['allowed_classes' => [Locale::class]]);
2039  $this->pageRenderer->updateState($pageRendererState);
2040  }
2041  if (!empty($this->config['INTincScript_ext']['assetCollectorState'])) {
2042  $assetCollectorState = unserialize($this->config['INTincScript_ext']['assetCollectorState'], ['allowed_classes' => false]);
2043  GeneralUtility::makeInstance(AssetCollector::class)->updateState($assetCollectorState);
2044  }
2045 
2047  $this->‪getTimeTracker()->‪push('Substitute header section');
2048  $this->‪INTincScript_loadJSCode();
2049  $this->‪generatePageTitle();
2050 
2051  $this->content = str_replace(
2052  [
2053  '<!--HD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
2054  '<!--FD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
2055  ],
2056  [
2057  implode(LF, $this->additionalHeaderData),
2058  implode(LF, $this->additionalFooterData),
2059  ],
2060  $this->pageRenderer->renderJavaScriptAndCssForProcessingOfUncachedContentObjects($this->content, $this->config['INTincScript_ext']['divKey'])
2061  );
2062  // Replace again, because header and footer data and page renderer replacements may introduce additional placeholders (see #44825)
2064  $this->‪setAbsRefPrefix();
2065  $this->‪getTimeTracker()->‪pull();
2066  }
2067 
2073  protected function ‪recursivelyReplaceIntPlaceholdersInContent(ServerRequestInterface $request)
2074  {
2075  do {
2076  $nonCacheableData = $this->config['INTincScript'];
2077  $this->‪processNonCacheableContentPartsAndSubstituteContentMarkers($nonCacheableData, $request);
2078  // Check if there were new items added to INTincScript during the previous execution:
2079  // array_diff_assoc throws notices if values are arrays but not strings. We suppress this here.
2080  $nonCacheableData = @array_diff_assoc($this->config['INTincScript'], $nonCacheableData);
2081  $reprocess = count($nonCacheableData) > 0;
2082  } while ($reprocess);
2083  }
2084 
2094  protected function ‪processNonCacheableContentPartsAndSubstituteContentMarkers(array $nonCacheableData, ServerRequestInterface $request)
2095  {
2096  $timeTracker = $this->‪getTimeTracker();
2097  $timeTracker->push('Split content');
2098  // Splits content with the key.
2099  $contentSplitByUncacheableMarkers = explode('<!--INT_SCRIPT.', $this->content);
2100  $this->content = '';
2101  $timeTracker->setTSlogMessage('Parts: ' . count($contentSplitByUncacheableMarkers), LogLevel::INFO);
2102  $timeTracker->pull();
2103  foreach ($contentSplitByUncacheableMarkers as $counter => $contentPart) {
2104  // If the split had a comment-end after 32 characters it's probably a split-string
2105  if (substr($contentPart, 32, 3) === '-->') {
2106  $nonCacheableKey = 'INT_SCRIPT.' . substr($contentPart, 0, 32);
2107  if (is_array($nonCacheableData[$nonCacheableKey] ?? false)) {
2108  $label = 'Include ' . $nonCacheableData[$nonCacheableKey]['type'];
2109  $timeTracker->push($label);
2110  $nonCacheableContent = '';
2111  $contentObjectRendererForNonCacheable = unserialize($nonCacheableData[$nonCacheableKey]['cObj']);
2112  if ($contentObjectRendererForNonCacheable instanceof ContentObjectRenderer) {
2113  $contentObjectRendererForNonCacheable->setRequest($request);
2114  $nonCacheableContent = match ($nonCacheableData[$nonCacheableKey]['type']) {
2115  'COA' => $contentObjectRendererForNonCacheable->cObjGetSingle('COA', $nonCacheableData[$nonCacheableKey]['conf']),
2116  'FUNC' => $contentObjectRendererForNonCacheable->cObjGetSingle('USER', $nonCacheableData[$nonCacheableKey]['conf']),
2117  'POSTUSERFUNC' => $contentObjectRendererForNonCacheable->callUserFunction($nonCacheableData[$nonCacheableKey]['postUserFunc'], $nonCacheableData[$nonCacheableKey]['conf'], $nonCacheableData[$nonCacheableKey]['content']),
2118  default => '',
2119  };
2120  }
2121  $this->content .= $nonCacheableContent;
2122  $this->content .= substr($contentPart, 35);
2123  $timeTracker->pull($nonCacheableContent);
2124  } else {
2125  $this->content .= substr($contentPart, 35);
2126  }
2127  } elseif ($counter) {
2128  // If it's not the first entry (which would be "0" of the array keys), then re-add the INT_SCRIPT part
2129  $this->content .= '<!--INT_SCRIPT.' . $contentPart;
2130  } else {
2131  $this->content .= $contentPart;
2132  }
2133  }
2134  // invokes permanent, general handlers
2135  foreach ($nonCacheableData as $item) {
2136  if (empty($item['permanent']) || empty($item['target'])) {
2137  continue;
2138  }
2139  $parameters = array_merge($item['parameters'] ?? [], ['content' => $this->content]);
2140  $this->content = GeneralUtility::callUserFunction($item['target'], $parameters) ?? $this->content;
2141  }
2142  }
2143 
2150  public function ‪INTincScript_loadJSCode()
2151  {
2152  // Prepare code and placeholders for additional header and footer files (and make sure that this isn't called twice)
2153  if ($this->‪isINTincScript() && !isset($this->config['INTincScript_ext'])) {
2154  $substituteHash = $this->‪uniqueHash();
2155  $this->config['INTincScript_ext']['divKey'] = $substituteHash;
2156  // Storing the header-data array
2157  $this->config['INTincScript_ext']['additionalHeaderData'] = $this->additionalHeaderData;
2158  // Storing the footer-data array
2159  $this->config['INTincScript_ext']['additionalFooterData'] = $this->additionalFooterData;
2160  // Clearing the array
2161  $this->additionalHeaderData = ['<!--HD_' . $substituteHash . '-->'];
2162  // Clearing the array
2163  $this->additionalFooterData = ['<!--FD_' . $substituteHash . '-->'];
2164  }
2165  }
2166 
2172  public function ‪isINTincScript()
2173  {
2174  return !empty($this->config['INTincScript']) && is_array($this->config['INTincScript']);
2175  }
2176 
2180  public function ‪applyHttpHeadersToResponse(ResponseInterface $response): ResponseInterface
2181  {
2182  $response = $response->withHeader('Content-Type', $this->contentType);
2183  // Set header for content language unless disabled
2184  $contentLanguage = (string)$this->‪language->getLocale();
2185  if (empty($this->config['config']['disableLanguageHeader'])) {
2186  $response = $response->withHeader('Content-Language', $contentLanguage);
2187  }
2188 
2189  // Add a Response header to show debug information if a page was fetched from cache
2190  if ($this->debugInformationHeader) {
2191  $response = $response->withHeader('X-TYPO3-Debug-Cache', $this->debugInformationHeader);
2192  }
2193 
2194  // Set cache related headers to client (used to enable proxy / client caching!)
2195  if (!empty($this->config['config']['sendCacheHeaders'])) {
2196  $headers = $this->‪getCacheHeaders();
2197  foreach ($headers as $header => $value) {
2198  $response = $response->withHeader($header, $value);
2199  }
2200  }
2201  // Set additional headers if any have been configured via TypoScript
2202  $additionalHeaders = $this->‪getAdditionalHeaders();
2203  foreach ($additionalHeaders as $headerConfig) {
2204  [$header, $value] = GeneralUtility::trimExplode(':', $headerConfig['header'], false, 2);
2205  if ($headerConfig['statusCode']) {
2206  $response = $response->withStatus((int)$headerConfig['statusCode']);
2207  }
2208  if ($headerConfig['replace']) {
2209  $response = $response->withHeader($header, $value);
2210  } else {
2211  $response = $response->withAddedHeader($header, $value);
2212  }
2213  }
2214  return $response;
2215  }
2216 
2220  protected function ‪getCacheHeaders(): array
2221  {
2222  // Getting status whether we can send cache control headers for proxy caching:
2223  $doCache = $this->‪isStaticCacheble();
2224  $isBackendUserLoggedIn = $this->context->getPropertyFromAspect('backend.user', 'isLoggedIn', false);
2225  $isInWorkspace = $this->context->getPropertyFromAspect('workspace', 'isOffline', false);
2226  // Finally, when backend users are logged in, do not send cache headers at all (Admin Panel might be displayed for instance).
2227  $isClientCachable = $doCache && !$isBackendUserLoggedIn && !$isInWorkspace;
2228  if ($isClientCachable) {
2229  $headers = [
2230  'Expires' => gmdate('D, d M Y H:i:s T', $this->cacheExpires),
2231  'ETag' => '"' . md5($this->content) . '"',
2232  'Cache-Control' => 'max-age=' . ($this->cacheExpires - ‪$GLOBALS['EXEC_TIME']),
2233  // no-cache
2234  'Pragma' => 'public',
2235  ];
2236  } else {
2237  // "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
2238  $headers = [
2239  'Cache-Control' => 'private, no-store',
2240  ];
2241  // Now, if a backend user is logged in, tell him in the Admin Panel log what the caching status would have been:
2242  if ($isBackendUserLoggedIn) {
2243  if ($doCache) {
2244  $this->‪getTimeTracker()->‪setTSlogMessage('Cache-headers with max-age "' . ($this->cacheExpires - ‪$GLOBALS['EXEC_TIME']) . '" would have been sent');
2245  } else {
2246  $reasonMsg = [];
2247  if ($this->no_cache) {
2248  $reasonMsg[] = 'Caching disabled (no_cache).';
2249  }
2250  if ($this->‪isINTincScript()) {
2251  $reasonMsg[] = '*_INT object(s) on page.';
2252  }
2253  if ($this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn', false)) {
2254  $reasonMsg[] = 'Frontend user logged in.';
2255  }
2256  $this->‪getTimeTracker()->‪setTSlogMessage('Cache-headers would disable proxy caching! Reason(s): "' . implode(' ', $reasonMsg) . '"', LogLevel::NOTICE);
2257  }
2258  }
2259  }
2260  return $headers;
2261  }
2262 
2273  public function ‪isStaticCacheble()
2274  {
2275  return !$this->no_cache && !$this->‪isINTincScript() && !$this->context->getAspect('frontend.user')->isUserOrGroupSet();
2276  }
2277 
2278  /********************************************
2279  *
2280  * Various internal API functions
2281  *
2282  *******************************************/
2289  public function ‪newCObj(ServerRequestInterface $request = null)
2290  {
2291  $this->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $this);
2292  $this->cObj->setRequest($request);
2293  $this->cObj->start($this->page, 'pages');
2294  }
2295 
2303  protected function ‪setAbsRefPrefix()
2304  {
2305  if (!$this->absRefPrefix) {
2306  return;
2307  }
2308  $encodedAbsRefPrefix = htmlspecialchars($this->absRefPrefix, ENT_QUOTES | ENT_HTML5);
2309  $search = [
2310  '"_assets/',
2311  '"typo3temp/',
2314  ];
2315  $replace = [
2316  '"' . $encodedAbsRefPrefix . '_assets/',
2317  '"' . $encodedAbsRefPrefix . 'typo3temp/',
2318  '"' . $encodedAbsRefPrefix . ‪PathUtility::stripPathSitePrefix(‪Environment::getExtensionsPath()) . '/',
2319  '"' . $encodedAbsRefPrefix . ‪PathUtility::stripPathSitePrefix(‪Environment::getFrameworkBasePath()) . '/',
2320  ];
2321  // Process additional directories
2322  $directories = GeneralUtility::trimExplode(',', ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalAbsRefPrefixDirectories'], true);
2323  foreach ($directories as $directory) {
2324  $search[] = '"' . $directory;
2325  $replace[] = '"' . $encodedAbsRefPrefix . $directory;
2326  }
2327  $this->content = str_replace(
2328  $search,
2329  $replace,
2330  $this->content
2331  );
2332  }
2333 
2342  public function ‪logDeprecatedTyposcript($typoScriptProperty, $explanation = '')
2343  {
2344  $explanationText = $explanation !== '' ? ' - ' . $explanation : '';
2345  $this->‪getTimeTracker()->‪setTSlogMessage($typoScriptProperty . ' is deprecated.' . $explanationText, LogLevel::WARNING);
2346  trigger_error('TypoScript property ' . $typoScriptProperty . ' is deprecated' . $explanationText, E_USER_DEPRECATED);
2347  }
2348 
2356  public function ‪uniqueHash($str = '')
2357  {
2358  return md5($this->‪uniqueString . '_' . $str . $this->uniqueCounter++);
2359  }
2360 
2367  public function ‪set_no_cache($reason = '', $internalRequest = false)
2368  {
2369  $warning = '';
2370  $context = [];
2371  if ($reason !== '') {
2372  $warning = '$TSFE->set_no_cache() was triggered. Reason: {reason}.';
2373  $context['reason'] = $reason;
2374  } else {
2375  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
2376  if (isset($trace[0]['class'])) {
2377  $context['class'] = $trace[0]['class'];
2378  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {class} on line {line}.';
2379  }
2380  if (isset($trace[0]['function'])) {
2381  $context['function'] = $trace[0]['function'];
2382  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {class}->{function} on line {line}.';
2383  }
2384  if ($context === []) {
2385  // Only store the filename, not the full path for safety reasons
2386  $context['file'] = basename($trace[0]['file']);
2387  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {file} on line {line}.';
2388  }
2389  $context['line'] = $trace[0]['line'];
2390  }
2391  if (!$internalRequest && ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['disableNoCacheParameter']) {
2392  $warning .= ' However, $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set, so it will be ignored!';
2393  $this->‪getTimeTracker()->‪setTSlogMessage($warning, LogLevel::NOTICE);
2394  } else {
2395  $warning .= ' Caching is disabled!';
2396  $this->‪disableCache();
2397  }
2398  $this->logger->notice($warning, $context);
2399  }
2400 
2406  protected function ‪disableCache()
2407  {
2408  $this->no_cache = true;
2409  }
2410 
2416  public function ‪set_cache_timeout_default($seconds)
2417  {
2418  $seconds = (int)$seconds;
2419  if ($seconds > 0) {
2420  $this->cacheTimeOutDefault = $seconds;
2421  }
2422  }
2423 
2427  public function ‪get_cache_timeout(): int
2428  {
2429  return GeneralUtility::makeInstance(CacheLifetimeCalculator::class)
2430  ->calculateLifetimeForPage(
2431  (int)$this->id,
2432  $this->page,
2433  $this->config['config'] ?? [],
2434  $this->cacheTimeOutDefault,
2435  $this->context
2436  );
2437  }
2438 
2439  /*********************************************
2440  *
2441  * Localization and character set conversion
2442  *
2443  *********************************************/
2450  public function ‪sL($input)
2451  {
2452  if ($this->languageService === null) {
2453  $this->languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)->createFromSiteLanguage($this->‪language);
2454  }
2455  return $this->languageService->sL($input);
2456  }
2457 
2461  public function ‪getRequestedId(): int
2462  {
2463  return $this->requestedId;
2464  }
2465 
2473  public function ‪releaseLocks(): void
2474  {
2475  $this->lock?->releaseLock('pages');
2476  }
2477 
2481  protected function ‪getAdditionalHeaders(): array
2482  {
2483  if (!isset($this->config['config']['additionalHeaders.'])) {
2484  return [];
2485  }
2486  $additionalHeaders = [];
2487  ksort($this->config['config']['additionalHeaders.']);
2488  foreach ($this->config['config']['additionalHeaders.'] as $options) {
2489  if (!is_array($options)) {
2490  continue;
2491  }
2492  $header = trim($options['header'] ?? '');
2493  if ($header === '') {
2494  continue;
2495  }
2496  $additionalHeaders[] = [
2497  'header' => $header,
2498  // "replace existing headers" is turned on by default, unless turned off
2499  'replace' => ($options['replace'] ?? '') !== '0',
2500  'statusCode' => (int)($options['httpResponseCode'] ?? 0) ?: null,
2501  ];
2502  }
2503  return $additionalHeaders;
2504  }
2505 
2509  protected function ‪logPageAccessFailure(string $message, ServerRequestInterface $request): void
2510  {
2511  $context = ['pageId' => ‪$this->id];
2512  if (($normalizedParams = $request->getAttribute('normalizedParams')) instanceof ‪NormalizedParams) {
2513  $context['requestUrl'] = $normalizedParams->getRequestUrl();
2514  }
2515  $this->logger->error($message, $context);
2516  }
2517 
2525  protected function ‪getBackendUser()
2526  {
2527  return ‪$GLOBALS['BE_USER'] ?? null;
2528  }
2529 
2533  protected function ‪getTimeTracker()
2534  {
2535  return GeneralUtility::makeInstance(TimeTracker::class);
2536  }
2537 
2538  public function ‪getLanguage(): ‪SiteLanguage
2539  {
2541  }
2542 
2543  public function ‪getSite(): ‪Site
2544  {
2545  return ‪$this->site;
2546  }
2547 
2548  public function ‪getContext(): ‪Context
2549  {
2550  return $this->context;
2551  }
2552 
2553  public function ‪getPageArguments(): PageArguments
2554  {
2555  return ‪$this->pageArguments;
2556  }
2557 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getRedirectUriForMountPoint
‪getRedirectUriForMountPoint(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1649
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\LANGUAGE_AND_FALLBACKS_NOT_AVAILABLE
‪const LANGUAGE_AND_FALLBACKS_NOT_AVAILABLE
Definition: PageAccessFailureReasons.php:44
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initPageRenderer
‪initPageRenderer()
Definition: TypoScriptFrontendController.php:455
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setAbsRefPrefix
‪setAbsRefPrefix()
Definition: TypoScriptFrontendController.php:2290
‪TYPO3\CMS\Core\TypoScript\IncludeTree\SysTemplateRepository
Definition: SysTemplateRepository.php:39
‪TYPO3\CMS\Core\Routing\PageArguments
Definition: PageArguments.php:26
‪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\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSetupConditionConstantSubstitutionVisitor
Definition: IncludeTreeSetupConditionConstantSubstitutionVisitor.php:36
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\fe_user
‪$this fe_user
Definition: TypoScriptFrontendController.php:438
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Context\LanguageAspectFactory
Definition: LanguageAspectFactory.php:27
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\recursivelyReplaceIntPlaceholdersInContent
‪recursivelyReplaceIntPlaceholdersInContent(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:2060
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getAdditionalHeaders
‪getAdditionalHeaders()
Definition: TypoScriptFrontendController.php:2468
‪TYPO3\CMS\Core\Locking\ResourceMutex
Definition: ResourceMutex.php:54
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\sL
‪string sL($input)
Definition: TypoScriptFrontendController.php:2437
‪TYPO3\CMS\Backend\FrontendBackendUserAuthentication
Definition: FrontendBackendUserAuthentication.php:34
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\generatePageTitle
‪string generatePageTitle()
Definition: TypoScriptFrontendController.php:1939
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\generatePage_preProcessing
‪generatePage_preProcessing()
Definition: TypoScriptFrontendController.php:1841
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getPageAccessFailureReasons
‪array getPageAccessFailureReasons(string $failureReasonCode=null)
Definition: TypoScriptFrontendController.php:921
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getSite
‪getSite()
Definition: TypoScriptFrontendController.php:2530
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$no_cache
‪bool $no_cache
Definition: TypoScriptFrontendController.php:130
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setContentType
‪setContentType($contentType)
Definition: TypoScriptFrontendController.php:476
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$MP
‪string $MP
Definition: TypoScriptFrontendController.php:201
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\disableCache
‪disableCache()
Definition: TypoScriptFrontendController.php:2393
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isGeneratePage
‪isGeneratePage()
Definition: TypoScriptFrontendController.php:1719
‪TYPO3\CMS\Core\Routing\PageArguments\getPageId
‪getPageId()
Definition: PageArguments.php:95
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\uniqueHash
‪string uniqueHash($str='')
Definition: TypoScriptFrontendController.php:2343
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\IncludeTreeTraverser
Definition: IncludeTreeTraverser.php:30
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\populatePageDataFromCache
‪populatePageDataFromCache(array $cachedData)
Definition: TypoScriptFrontendController.php:1412
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SHORTCUT
‪const DOKTYPE_SHORTCUT
Definition: PageRepository.php:110
‪TYPO3\CMS\Core\Context\LanguageAspectFactory\createFromSiteLanguage
‪static createFromSiteLanguage(SiteLanguage $language)
Definition: LanguageAspectFactory.php:31
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageArguments
‪PageArguments $pageArguments
Definition: TypoScriptFrontendController.php:123
‪TYPO3\CMS\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:40
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:26
‪TYPO3\CMS\Core\Site\Entity\Site\getAttribute
‪mixed getAttribute(string $attributeName)
Definition: Site.php:310
‪TYPO3\CMS\Frontend\Event\BeforePageIsResolvedEvent
Definition: BeforePageIsResolvedEvent.php:31
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Core\Environment\getExtensionsPath
‪static getExtensionsPath()
Definition: Environment.php:253
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getContext
‪getContext()
Definition: TypoScriptFrontendController.php:2535
‪TYPO3\CMS\Core\Localization\Locales
Definition: Locales.php:33
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeAstBuilderVisitor
Definition: IncludeTreeAstBuilderVisitor.php:39
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setRegisterValueForSysLastChanged
‪setRegisterValueForSysLastChanged(array $page)
Definition: TypoScriptFrontendController.php:1809
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$type
‪int string $type
Definition: TypoScriptFrontendController.php:115
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\uniqueString
‪$this uniqueString
Definition: TypoScriptFrontendController.php:439
‪TYPO3\CMS\Core\Type\Bitmask\PageTranslationVisibility
Definition: PageTranslationVisibility.php:30
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageAccessFailureHistory
‪array $pageAccessFailureHistory
Definition: TypoScriptFrontendController.php:196
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\ACCESS_DENIED_INVALID_PAGETYPE
‪const ACCESS_DENIED_INVALID_PAGETYPE
Definition: PageAccessFailureReasons.php:52
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:55
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getRequestedId
‪getRequestedId()
Definition: TypoScriptFrontendController.php:2448
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isStaticCacheble
‪bool isStaticCacheble()
Definition: TypoScriptFrontendController.php:2260
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getFromCache
‪ServerRequestInterface getFromCache(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1008
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\settingLanguage
‪settingLanguage(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1514
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$page
‪array $page
Definition: TypoScriptFrontendController.php:161
‪TYPO3\CMS\Core\Context\Context\setAspect
‪setAspect(string $name, AspectInterface $aspect)
Definition: Context.php:152
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\clearPageCacheContent
‪clearPageCacheContent()
Definition: TypoScriptFrontendController.php:1764
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:27
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\generatePage_postProcessing
‪generatePage_postProcessing(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1906
‪TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent
Definition: AfterCacheableContentIsGeneratedEvent.php:29
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_MOUNTPOINT
‪const DOKTYPE_MOUNTPOINT
Definition: PageRepository.php:112
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getLanguage
‪getLanguage()
Definition: TypoScriptFrontendController.php:2525
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:46
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\isINTincScript
‪bool isINTincScript()
Definition: TypoScriptFrontendController.php:2159
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\processNonCacheableContentPartsAndSubstituteContentMarkers
‪processNonCacheableContentPartsAndSubstituteContentMarkers(array $nonCacheableData, ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:2081
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\pull
‪pull(string $content='')
Definition: TimeTracker.php:153
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeConditionIncludeListAccumulatorVisitor
Definition: IncludeTreeConditionIncludeListAccumulatorVisitor.php:32
‪TYPO3\CMS\Core\Error\Http\PageNotFoundException
Definition: PageNotFoundException.php:24
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\ACCESS_DENIED_HOST_PAGE_MISMATCH
‪const ACCESS_DENIED_HOST_PAGE_MISMATCH
Definition: PageAccessFailureReasons.php:51
‪TYPO3\CMS\Core\Http\ImmediateResponseException\getResponse
‪Response getResponse()
Definition: ImmediateResponseException.php:49
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$fe_user
‪FrontendUserAuthentication $fe_user
Definition: TypoScriptFrontendController.php:207
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\determineId
‪determineId(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:527
‪TYPO3\CMS\Frontend\Cache\CacheLifetimeCalculator
Definition: CacheLifetimeCalculator.php:39
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getPageArguments
‪getPageArguments()
Definition: TypoScriptFrontendController.php:2540
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\ACCESS_DENIED_PAGE_NOT_RESOLVED
‪const ACCESS_DENIED_PAGE_NOT_RESOLVED
Definition: PageAccessFailureReasons.php:49
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$originalShortcutPage
‪array $originalShortcutPage
Definition: TypoScriptFrontendController.php:180
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\calculateLinkVars
‪calculateLinkVars(array $queryParams)
Definition: TypoScriptFrontendController.php:1631
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\LANGUAGE_DEFAULT_NOT_AVAILABLE
‪const LANGUAGE_DEFAULT_NOT_AVAILABLE
Definition: PageAccessFailureReasons.php:45
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getBackendUser
‪FrontendBackendUserAuthentication null getBackendUser()
Definition: TypoScriptFrontendController.php:2512
‪TYPO3\CMS\Frontend\Event\AfterPageAndLanguageIsResolvedEvent
Definition: AfterPageAndLanguageIsResolvedEvent.php:32
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getPageCacheTags
‪getPageCacheTags()
Definition: TypoScriptFrontendController.php:1828
‪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:31
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\LANGUAGE_NOT_AVAILABLE
‪const LANGUAGE_NOT_AVAILABLE
Definition: PageAccessFailureReasons.php:42
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\PAGE_NOT_FOUND
‪const PAGE_NOT_FOUND
Definition: PageAccessFailureReasons.php:28
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$pageNotFound
‪int $pageNotFound
Definition: TypoScriptFrontendController.php:191
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SPACER
‪const DOKTYPE_SPACER
Definition: PageRepository.php:113
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\RENDERING_INSTRUCTIONS_NOT_CONFIGURED
‪const RENDERING_INSTRUCTIONS_NOT_CONFIGURED
Definition: PageAccessFailureReasons.php:34
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\evaluatePageNotFound
‪evaluatePageNotFound(int $pageNotFoundNumber, ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:596
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initializeContext
‪initializeContext(Context $context)
Definition: TypoScriptFrontendController.php:444
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setPageCacheContent
‪setPageCacheContent(string $content, array $data, int $expirationTstamp)
Definition: TypoScriptFrontendController.php:1732
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$contentPid
‪int $contentPid
Definition: TypoScriptFrontendController.php:167
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_BE_USER_SECTION
‪const DOKTYPE_BE_USER_SECTION
Definition: PageRepository.php:111
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\preparePageContentGeneration
‪preparePageContentGeneration(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1873
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$site
‪Site $site
Definition: TypoScriptFrontendController.php:117
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\ACCESS_DENIED_SUBSECTION_NOT_RESOLVED
‪const ACCESS_DENIED_SUBSECTION_NOT_RESOLVED
Definition: PageAccessFailureReasons.php:50
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\shouldAcquireCacheData
‪bool shouldAcquireCacheData(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1452
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$originalMountPointPage
‪array $originalMountPointPage
Definition: TypoScriptFrontendController.php:174
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SYSFOLDER
‪const DOKTYPE_SYSFOLDER
Definition: PageRepository.php:114
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getPageAndRootline
‪getPageAndRootline(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:671
‪TYPO3\CMS\Core\Context\LanguageAspect
Definition: LanguageAspect.php:57
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initializeContext
‪array< string, $config=array();protected int $cacheTimeOutDefault=0;protected bool $pageContentWasLoadedFromCache=false;protected int $cacheExpires=0;public array|string $pSetup='';public string $newHash='';protected bool $no_cacheBeforePageGen=false;public array $additionalHeaderData=array();public array $additionalFooterData=array();public string $absRefPrefix='';public string $linkVars='';public array $applicationData=[];public array $register=[];public array $registerStack=[];public array $recordRegister=[];public string $currentRecord='';protected int $uniqueCounter=0;protected string $uniqueString='';public ContentObjectRenderer $cObj;public string $content='';public ?array $lastImgResourceInfo=null;protected ?LanguageService $languageService=null;public ?ResourceMutex $lock=null;protected ?PageRenderer $pageRenderer=null;protected FrontendInterface $pageCache;protected array $pageCacheTags=[];protected string $contentType='text/html;charset=utf-8';protected int $requestedId=0;protected Context $context;protected string $debugInformationHeader='';public __construct(Context $context, Site $site, SiteLanguage $siteLanguage, PageArguments $pageArguments, FrontendUserAuthentication $frontendUser) { $this-> initializeContext($context)
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\get_cache_timeout
‪get_cache_timeout()
Definition: TypoScriptFrontendController.php:2414
‪TYPO3\CMS\Frontend\Event\AfterPageWithRootLineIsResolvedEvent
Definition: AfterPageWithRootLineIsResolvedEvent.php:31
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getRedirectUriForShortcut
‪getRedirectUriForShortcut(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1667
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\releaseLocks
‪releaseLocks()
Definition: TypoScriptFrontendController.php:2460
‪TYPO3\CMS\Core\Http\PropagateResponseException
Definition: PropagateResponseException.php:48
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getRelevantParametersForCachingFromPageArguments
‪getRelevantParametersForCachingFromPageArguments(PageArguments $pageArguments)
Definition: TypoScriptFrontendController.php:979
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\LANGUAGE_NOT_AVAILABLE_STRICT_MODE
‪const LANGUAGE_NOT_AVAILABLE_STRICT_MODE
Definition: PageAccessFailureReasons.php:43
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getWebsiteTitle
‪getWebsiteTitle()
Definition: TypoScriptFrontendController.php:2003
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\createHashBase
‪string createHashBase(array $sysTemplateRows, array $constantConditionList, array $setupConditionList)
Definition: TypoScriptFrontendController.php:1471
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\printTitle
‪string printTitle(string $pageTitle, bool $noPageTitle=false, bool $showPageTitleFirst=false, string $pageTitleSeparator='', bool $showWebsiteTitle=true)
Definition: TypoScriptFrontendController.php:1986
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\logDeprecatedTyposcript
‪logDeprecatedTyposcript($typoScriptProperty, $explanation='')
Definition: TypoScriptFrontendController.php:2329
‪TYPO3\CMS\Core\Core\Environment\getFrameworkBasePath
‪static getFrameworkBasePath()
Definition: Environment.php:245
‪TYPO3\CMS\Core\Error\Http\ShortcutTargetPageNotFoundException
Definition: ShortcutTargetPageNotFoundException.php:24
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$id
‪int $id
Definition: TypoScriptFrontendController.php:108
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getCacheHeaders
‪getCacheHeaders()
Definition: TypoScriptFrontendController.php:2207
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\logPageAccessFailure
‪logPageAccessFailure(string $message, ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:2496
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\addCacheTags
‪addCacheTags(array $tags)
Definition: TypoScriptFrontendController.php:1823
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:102
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage(string $content, string $logLevel=LogLevel::INFO)
Definition: TimeTracker.php:173
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\TypoScript\AST\Node\ChildNode
Definition: ChildNode.php:24
‪TYPO3\CMS\Core\Error\Http\AbstractServerErrorException
Definition: AbstractServerErrorException.php:22
‪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\getUriToCurrentPageForRedirect
‪getUriToCurrentPageForRedirect(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1696
‪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:2137
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:90
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\ConditionVerdictAwareIncludeTreeTraverser
Definition: ConditionVerdictAwareIncludeTreeTraverser.php:38
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setSysLastChanged
‪setSysLastChanged()
Definition: TypoScriptFrontendController.php:1779
‪TYPO3\CMS\Core\Routing\PageArguments\getPageType
‪getPageType()
Definition: PageArguments.php:100
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\set_cache_timeout_default
‪set_cache_timeout_default($seconds)
Definition: TypoScriptFrontendController.php:2403
‪TYPO3\CMS\Frontend\Controller
Definition: ErrorController.php:18
‪TYPO3\CMS\Core\Exception\Page\RootLineException
Definition: RootLineException.php:25
‪TYPO3\CMS\Core\Type\DocType
‪DocType
Definition: DocType.php:27
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:24
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\updateRootLinesWithTranslations
‪updateRootLinesWithTranslations()
Definition: TypoScriptFrontendController.php:1616
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
Definition: FrontendUserAuthentication.php:33
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\newCObj
‪newCObj(ServerRequestInterface $request=null)
Definition: TypoScriptFrontendController.php:2276
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:61
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:51
‪TYPO3\CMS\Core\TypoScript\FrontendTypoScript
Definition: FrontendTypoScript.php:29
‪TYPO3\CMS\Frontend\Event\ShouldUseCachedPageDataIfAvailableEvent
Definition: ShouldUseCachedPageDataIfAvailableEvent.php:28
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\applyHttpHeadersToResponse
‪applyHttpHeadersToResponse(ResponseInterface $response)
Definition: TypoScriptFrontendController.php:2167
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$language
‪SiteLanguage $language
Definition: TypoScriptFrontendController.php:118
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\setPageArguments
‪setPageArguments(PageArguments $pageArguments)
Definition: TypoScriptFrontendController.php:961
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getTimeTracker
‪TimeTracker getTimeTracker()
Definition: TypoScriptFrontendController.php:2520
‪TYPO3\CMS\Core\Http\ImmediateResponseException
Definition: ImmediateResponseException.php:35
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\resolveContentPid
‪int resolveContentPid(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:1857
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$rootLine
‪array $rootLine
Definition: TypoScriptFrontendController.php:156
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\set_no_cache
‪set_no_cache($reason='', $internalRequest=false)
Definition: TypoScriptFrontendController.php:2354
‪TYPO3\CMS\Frontend\Aspect\PreviewAspect
Definition: PreviewAspect.php:30
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:32
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\language
‪$this language
Definition: TypoScriptFrontendController.php:436
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\site
‪$this site
Definition: TypoScriptFrontendController.php:435
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\initCaches
‪initCaches()
Definition: TypoScriptFrontendController.php:489
‪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\Core\Domain\Access\RecordAccessVoter
Definition: RecordAccessVoter.php:29
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\ROOTLINE_BROKEN
‪const ROOTLINE_BROKEN
Definition: PageAccessFailureReasons.php:29
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\$sys_page
‪PageRepository string $sys_page
Definition: TypoScriptFrontendController.php:186
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\push
‪push(string $tslabel, string $value='')
Definition: TimeTracker.php:126
‪TYPO3\CMS\Core\Context\UserAspect
Definition: UserAspect.php:37
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:38
‪TYPO3\CMS\Frontend\Event\AfterCachedPageIsPersistedEvent
Definition: AfterCachedPageIsPersistedEvent.php:32
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\checkRootlineForIncludeSection
‪checkRootlineForIncludeSection()
Definition: TypoScriptFrontendController.php:880
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeConditionMatcherVisitor
Definition: IncludeTreeConditionMatcherVisitor.php:44
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\INTincScript
‪INTincScript(ServerRequestInterface $request)
Definition: TypoScriptFrontendController.php:2018
‪TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\RENDERING_INSTRUCTIONS_NOT_FOUND
‪const RENDERING_INSTRUCTIONS_NOT_FOUND
Definition: PageAccessFailureReasons.php:33