2 declare(strict_types
= 1);
4 namespace TYPO3\CMS\Frontend\Http
;
7 * This file is part of the TYPO3 CMS project.
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
16 * The TYPO3 project - inspiring people to share!
19 use Psr\Http\Message\ResponseInterface
;
20 use Psr\Http\Message\ServerRequestInterface
;
21 use Psr\Http\Server\RequestHandlerInterface
as PsrRequestHandlerInterface
;
22 use TYPO3\CMS\Core\Core\Environment
;
23 use TYPO3\CMS\Core\Http\NullResponse
;
24 use TYPO3\CMS\Core\Http\RequestHandlerInterface
;
25 use TYPO3\CMS\Core\Http\Response
;
26 use TYPO3\CMS\Core\Page\PageRenderer
;
27 use TYPO3\CMS\Core\Site\Entity\SiteLanguage
;
28 use TYPO3\CMS\Core\TimeTracker\TimeTracker
;
29 use TYPO3\CMS\Core\Type\File\ImageInfo
;
30 use TYPO3\CMS\Core\TypoScript\TypoScriptService
;
31 use TYPO3\CMS\Core\Utility\ArrayUtility
;
32 use TYPO3\CMS\Core\Utility\GeneralUtility
;
33 use TYPO3\CMS\Core\Utility\PathUtility
;
34 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
;
35 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
;
36 use TYPO3\CMS\Frontend\
Resource\FilePathSanitizer
;
39 * This is the main entry point of the TypoScript driven standard front-end.
41 * "handle()" is called when all PSR-15 middlewares have been set up the PSR-7 ServerRequest object and the following
42 * things have been evaluated
43 * - correct page ID, page type (typeNum), rootline, MP etc.
44 * - info if is cached content already available
46 * - proper TypoScript which should be processed.
48 * Then, this class is able to render the actual HTTP body part built via TypoScript. Here this is split into two parts:
49 * - Everything included in <body>, done via page.10, page.20 etc.
50 * - Everything around.
52 * If the content has been built together within the cache (cache_pages), it is fetched directly, and
53 * any so-called "uncached" content is generated again.
55 * Some further hooks allow to post-processing the content.
57 * Then the right HTTP response headers are compiled together and sent as well.
59 class RequestHandler
implements RequestHandlerInterface
, PsrRequestHandlerInterface
62 * Instance of the timetracker
65 protected $timeTracker;
68 * Handles a frontend request
70 * @param ServerRequestInterface $request
71 * @return ResponseInterface
73 public function handleRequest(ServerRequestInterface
$request): ResponseInterface
75 return $this->handle($request);
79 * Puts parameters that have been added or removed from the global _GET or _POST arrays
80 * into the given request (however, the PSR-7 request information takes precedence).
82 * @param ServerRequestInterface $request
83 * @return ServerRequestInterface
85 protected function addModifiedGlobalsToIncomingRequest(ServerRequestInterface
$request): ServerRequestInterface
87 $originalGetParameters = $request->getAttribute('_originalGetParameters', null);
88 if ($originalGetParameters !== null && !empty($_GET) && $_GET !== $originalGetParameters) {
89 // Find out what has been changed.
90 $modifiedGetParameters = ArrayUtility
::arrayDiffAssocRecursive($_GET ??
[], $originalGetParameters);
91 if (!empty($modifiedGetParameters)) {
92 $queryParams = array_replace_recursive($modifiedGetParameters, $request->getQueryParams());
93 $request = $request->withQueryParams($queryParams);
94 $GLOBALS['TYPO3_REQUEST'] = $request;
95 $this->timeTracker
->setTSlogMessage('GET parameters have been modified during Request building in a hook.');
98 // do same for $_POST if the request is a POST request
99 $originalPostParameters = $request->getAttribute('_originalPostParameters', null);
100 if ($request->getMethod() === 'POST' && $originalPostParameters !== null && !empty($_POST) && $_POST !== $originalPostParameters) {
101 // Find out what has been changed
102 $modifiedPostParameters = ArrayUtility
::arrayDiffAssocRecursive($_POST ??
[], $originalPostParameters);
103 if (!empty($modifiedPostParameters)) {
104 $parsedBody = array_replace_recursive($modifiedPostParameters, $request->getParsedBody());
105 $request = $request->withParsedBody($parsedBody);
106 $GLOBALS['TYPO3_REQUEST'] = $request;
107 $this->timeTracker
->setTSlogMessage('POST parameters have been modified during Request building in a hook.');
114 * Sets the global GET and POST to the values, so if people access $_GET and $_POST
115 * Within hooks starting NOW (e.g. cObject), they get the "enriched" data from query params.
117 * This needs to be run after the request object has been enriched with modified GET/POST variables.
119 * @param ServerRequestInterface $request
120 * @internal this safety net will be removed in TYPO3 v10.0.
122 protected function resetGlobalsToCurrentRequest(ServerRequestInterface
$request)
124 if ($request->getQueryParams() !== $_GET) {
125 $queryParams = $request->getQueryParams();
126 $_GET = $queryParams;
127 $GLOBALS['HTTP_GET_VARS'] = $_GET;
129 if ($request->getMethod() === 'POST') {
130 $parsedBody = $request->getParsedBody();
131 if (is_array($parsedBody) && $parsedBody !== $_POST) {
132 $_POST = $parsedBody;
133 $GLOBALS['HTTP_POST_VARS'] = $_POST;
138 * Handles a frontend request, after finishing running middlewares
140 * @param ServerRequestInterface $request
141 * @return ResponseInterface|null
143 public function handle(ServerRequestInterface
$request): ResponseInterface
145 // Fetch the initialized time tracker object
146 $this->timeTracker
= GeneralUtility
::makeInstance(TimeTracker
::class);
147 /** @var TypoScriptFrontendController $controller */
148 $controller = $GLOBALS['TSFE'];
150 // safety net, will be removed in TYPO3 v10.0. Aligns $_GET/$_POST to the incoming request.
151 $request = $this->addModifiedGlobalsToIncomingRequest($request);
152 $this->resetGlobalsToCurrentRequest($request);
155 if ($controller->isGeneratePage()) {
156 $this->timeTracker
->push('Page generation');
157 $controller->generatePage_preProcessing();
158 $controller->preparePageContentGeneration($request);
160 // Content generation
161 $this->timeTracker
->incStackPointer();
162 $this->timeTracker
->push($controller->sPre
, 'PAGE');
164 // If 'disableAllHeaderCode' is set, all the header-code is discarded
165 if ($controller->config
['config']['disableAllHeaderCode'] ??
false) {
166 $controller->content
= $this->generatePageContent($controller);
168 $controller->content
= $this->generatePageContentWithHeader($controller, $request->getAttribute('language', null));
171 $this->timeTracker
->pull($this->timeTracker
->LR ?
$controller->content
: '');
172 $this->timeTracker
->decStackPointer();
174 $controller->setAbsRefPrefix();
175 $controller->generatePage_postProcessing();
176 $this->timeTracker
->pull();
178 $controller->releaseLocks();
180 // Render non-cached page parts by replacing placeholders which are taken from cache or added during page generation
181 if ($controller->isINTincScript()) {
182 if (!$controller->isGeneratePage()) {
183 // When page was generated, this was already called. Avoid calling this twice.
184 $controller->preparePageContentGeneration($request);
186 $this->timeTracker
->push('Non-cached objects');
187 $controller->INTincScript();
188 $this->timeTracker
->pull();
191 // Create a Response object when sending content
192 $response = new Response();
195 $isOutputting = $controller->isOutputting();
197 $this->timeTracker
->push('Print Content');
198 $response = $controller->applyHttpHeadersToResponse($response);
199 $controller->processContentForOutput();
200 $this->timeTracker
->pull();
202 // Store session data for fe_users
203 $controller->fe_user
->storeSessionData();
205 // @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0.
206 $redirectResponse = $controller->redirectToExternalUrl(true);
207 if ($redirectResponse instanceof ResponseInterface
) {
208 $controller->sendHttpHeadersDirectly();
209 return $redirectResponse;
213 $GLOBALS['TYPO3_MISC']['microtime_end'] = microtime(true);
214 if ($isOutputting && ($controller->config
['config']['debug'] ??
!empty($GLOBALS['TYPO3_CONF_VARS']['FE']['debug']))) {
215 $response = $response->withHeader('X-TYPO3-Parsetime', $this->timeTracker
->getParseTime() . 'ms');
218 // Hook for "end-of-frontend"
219 $_params = ['pObj' => &$controller];
220 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_eofe'] ??
[] as $_funcRef) {
221 GeneralUtility
::callUserFunction($_funcRef, $_params, $controller);
224 // Finish time tracking (started in TYPO3\CMS\Frontend\Middleware\TimeTrackerInitialization)
225 $this->timeTracker
->pull();
228 $response->getBody()->write($controller->content
);
229 // if any code set a response code that is not 200 clear the cache's content
230 // if we fail to do so we would deliver cache content with a wrong header, which causes big mess.
231 if (http_response_code() !== 200 ||
$response->getStatusCode() !== 200) {
232 $controller->clearPageCacheContent();
236 return $isOutputting ?
$response : new NullResponse();
240 * Generates the main content part within <body> tags (except JS files/CSS files).
242 * @param TypoScriptFrontendController $controller
245 protected function generatePageContent(TypoScriptFrontendController
$controller): string
247 $pageContent = $controller->cObj
->cObjGet($controller->pSetup
) ?
: '';
248 if ($controller->pSetup
['wrap'] ??
false) {
249 $pageContent = $controller->cObj
->wrap($pageContent, $controller->pSetup
['wrap']);
251 if ($controller->pSetup
['stdWrap.'] ??
false) {
252 $pageContent = $controller->cObj
->stdWrap($pageContent, $controller->pSetup
['stdWrap.']);
258 * Rendering normal HTML-page with header by wrapping the generated content ($pageContent) in body-tags and setting the header accordingly.
259 * Render HTML page with header parts (<head> tag content and wrap around <body> tag) - this is done
260 * after the "main" page Content, since some JS may be inserted at that point.
262 * @param TypoScriptFrontendController $controller
263 * @param SiteLanguage|null $siteLanguage
266 protected function generatePageContentWithHeader(TypoScriptFrontendController
$controller, ?SiteLanguage
$siteLanguage): string
268 // Generate the page content, this has to be first, as some additional TSFE-related code could have been written
269 $pageContent = $this->generatePageContent($controller);
270 $pageRenderer = $this->getPageRenderer();
271 if ($controller->config
['config']['moveJsFromHeaderToFooter'] ??
false) {
272 $pageRenderer->enableMoveJsFromHeaderToFooter();
274 if ($controller->config
['config']['pageRendererTemplateFile'] ??
false) {
276 $file = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($controller->config
['config']['pageRendererTemplateFile']);
277 $pageRenderer->setTemplateFile($file);
278 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
282 $headerComment = trim($controller->config
['config']['headerComment'] ??
'');
283 if ($headerComment) {
284 $pageRenderer->addInlineComment("\t" . str_replace(LF
, LF
. "\t", $headerComment) . LF
);
287 $theCharset = $controller->metaCharset
;
288 // Reset the content variables:
289 $controller->content
= '';
290 $htmlTagAttributes = [];
291 $htmlLang = $controller->config
['config']['htmlTag_langKey'] ??
($controller->sys_language_isocode ?
: 'en');
292 $direction = $controller->config
['config']['htmlTag_dir'] ??
null;
293 if ($siteLanguage !== null) {
294 $direction = $siteLanguage->getDirection();
295 $htmlLang = $siteLanguage->getTwoLetterIsoCode();
299 $htmlTagAttributes['dir'] = htmlspecialchars($direction);
301 // Setting document type:
304 // Part 1: XML prologue
305 switch ((string)($controller->config
['config']['xmlprologue'] ??
'')) {
307 $xmlDocument = false;
310 $docTypeParts[] = '<?xml version="1.0" encoding="' . $theCharset . '"?>';
313 $docTypeParts[] = '<?xml version="1.1" encoding="' . $theCharset . '"?>';
316 if ($controller->xhtmlVersion
) {
317 $docTypeParts[] = '<?xml version="1.0" encoding="' . $theCharset . '"?>';
319 $xmlDocument = false;
323 $docTypeParts[] = $controller->config
['config']['xmlprologue'];
326 $doctype = $controller->config
['config']['doctype'] ??
null;
330 $docTypeParts[] = '<!DOCTYPE html
331 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
332 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
335 $docTypeParts[] = '<!DOCTYPE html
336 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
337 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
340 $docTypeParts[] = '<!DOCTYPE html
341 PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
342 "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">';
345 $docTypeParts[] = '<!DOCTYPE html
346 PUBLIC "-//W3C//DTD XHTML 1.1//EN"
347 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
349 case 'xhtml+rdfa_10':
350 $docTypeParts[] = '<!DOCTYPE html
351 PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
352 "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">';
355 $docTypeParts[] = '<!DOCTYPE html>';
357 $pageRenderer->setMetaCharsetTag('<meta charset="|" />');
359 $pageRenderer->setMetaCharsetTag('<meta charset="|">');
365 $docTypeParts[] = $doctype;
368 $docTypeParts[] = '<!DOCTYPE html>';
370 $pageRenderer->setMetaCharsetTag('<meta charset="|" />');
372 $pageRenderer->setMetaCharsetTag('<meta charset="|">');
375 if ($controller->xhtmlVersion
) {
376 $htmlTagAttributes['xml:lang'] = $htmlLang;
378 if ($controller->xhtmlVersion
< 110 ||
$doctype === 'html5') {
379 $htmlTagAttributes['lang'] = $htmlLang;
381 if ($controller->xhtmlVersion ||
$doctype === 'html5' && $xmlDocument) {
382 // We add this to HTML5 to achieve a slightly better backwards compatibility
383 $htmlTagAttributes['xmlns'] = 'http://www.w3.org/1999/xhtml';
384 if (is_array($controller->config
['config']['namespaces.'])) {
385 foreach ($controller->config
['config']['namespaces.'] as $prefix => $uri) {
386 // $uri gets htmlspecialchared later
387 $htmlTagAttributes['xmlns:' . htmlspecialchars($prefix)] = $uri;
391 // Swap XML and doctype order around (for MSIE / Opera standards compliance)
392 if ($controller->config
['config']['doctypeSwitch'] ??
false) {
393 $docTypeParts = array_reverse($docTypeParts);
395 // Adding doctype parts:
396 if (!empty($docTypeParts)) {
397 $pageRenderer->setXmlPrologAndDocType(implode(LF
, $docTypeParts));
399 // Begin header section:
400 $htmlTag = $this->generateHtmlTag($htmlTagAttributes, $controller->config
['config'] ??
[], $controller->cObj
);
401 $pageRenderer->setHtmlTag($htmlTag);
403 $headTag = $controller->pSetup
['headTag'] ??
'<head>';
404 if (isset($controller->pSetup
['headTag.'])) {
405 $headTag = $controller->cObj
->stdWrap($headTag, $controller->pSetup
['headTag.']);
407 $pageRenderer->setHeadTag($headTag);
408 // Setting charset meta tag:
409 $pageRenderer->setCharSet($theCharset);
410 $pageRenderer->addInlineComment(' This website is powered by TYPO3 - inspiring people to share!
411 TYPO3 is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.
412 TYPO3 is copyright ' . TYPO3_copyright_year
. ' of Kasper Skaarhoj. Extensions are copyright of their respective owners.
413 Information and contribution at ' . TYPO3_URL_GENERAL
. '
415 if ($controller->baseUrl
) {
416 $pageRenderer->setBaseUrl($controller->baseUrl
);
418 if ($controller->pSetup
['shortcutIcon'] ??
false) {
420 $favIcon = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($controller->pSetup
['shortcutIcon']);
421 $iconFileInfo = GeneralUtility
::makeInstance(ImageInfo
::class, Environment
::getPublicPath() . '/' . $favIcon);
422 if ($iconFileInfo->isFile()) {
423 $iconMimeType = $iconFileInfo->getMimeType();
425 $iconMimeType = ' type="' . $iconMimeType . '"';
426 $pageRenderer->setIconMimeType($iconMimeType);
428 $pageRenderer->setFavIcon(PathUtility
::getAbsoluteWebPath($controller->absRefPrefix
. $favIcon));
430 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
434 // Including CSS files
435 if (isset($controller->tmpl
->setup
['plugin.']) && is_array($controller->tmpl
->setup
['plugin.'])) {
436 $stylesFromPlugins = '';
437 foreach ($controller->tmpl
->setup
['plugin.'] as $key => $iCSScode) {
438 if (is_array($iCSScode)) {
439 if ($iCSScode['_CSS_DEFAULT_STYLE'] && empty($controller->config
['config']['removeDefaultCss'])) {
440 if (isset($iCSScode['_CSS_DEFAULT_STYLE.'])) {
441 $cssDefaultStyle = $controller->cObj
->stdWrap($iCSScode['_CSS_DEFAULT_STYLE'], $iCSScode['_CSS_DEFAULT_STYLE.']);
443 $cssDefaultStyle = $iCSScode['_CSS_DEFAULT_STYLE'];
445 $stylesFromPlugins .= '/* default styles for extension "' . substr($key, 0, -1) . '" */' . LF
. $cssDefaultStyle . LF
;
447 if ($iCSScode['_CSS_PAGE_STYLE'] && empty($controller->config
['config']['removePageCss'])) {
448 $cssPageStyle = implode(LF
, $iCSScode['_CSS_PAGE_STYLE']);
449 if (isset($iCSScode['_CSS_PAGE_STYLE.'])) {
450 $cssPageStyle = $controller->cObj
->stdWrap($cssPageStyle, $iCSScode['_CSS_PAGE_STYLE.']);
452 $cssPageStyle = '/* specific page styles for extension "' . substr($key, 0, -1) . '" */' . LF
. $cssPageStyle;
453 $this->addCssToPageRenderer($controller, $cssPageStyle, true, 'InlinePageCss');
457 if (!empty($stylesFromPlugins)) {
458 $this->addCssToPageRenderer($controller, $stylesFromPlugins, false, 'InlineDefaultCss');
461 /**********************************************************************/
462 /* config.includeCSS / config.includeCSSLibs
463 /**********************************************************************/
464 if (isset($controller->pSetup
['includeCSS.']) && is_array($controller->pSetup
['includeCSS.'])) {
465 foreach ($controller->pSetup
['includeCSS.'] as $key => $CSSfile) {
466 if (!is_array($CSSfile)) {
467 $cssFileConfig = &$controller->pSetup
['includeCSS.'][$key . '.'];
468 if (isset($cssFileConfig['if.']) && !$controller->cObj
->checkIf($cssFileConfig['if.'])) {
471 if ($cssFileConfig['external']) {
475 $ss = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($CSSfile);
476 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
481 if ($cssFileConfig['import']) {
482 if (!$cssFileConfig['external'] && $ss[0] !== '/') {
483 // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende)
484 $ss = GeneralUtility
::dirname(GeneralUtility
::getIndpEnv('SCRIPT_NAME')) . '/' . $ss;
486 $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), (bool)$cssFileConfig['forceOnTop']);
488 $pageRenderer->addCssFile(
490 $cssFileConfig['alternate'] ?
'alternate stylesheet' : 'stylesheet',
491 $cssFileConfig['media'] ?
: 'all',
492 $cssFileConfig['title'] ?
: '',
493 $cssFileConfig['external'] ?
false : empty($cssFileConfig['disableCompression']),
494 (bool)$cssFileConfig['forceOnTop'],
495 $cssFileConfig['allWrap'],
496 (bool)$cssFileConfig['excludeFromConcatenation'] ||
(bool)$cssFileConfig['inline'],
497 $cssFileConfig['allWrap.']['splitChar'],
498 $cssFileConfig['inline']
500 unset($cssFileConfig);
506 if (isset($controller->pSetup
['includeCSSLibs.']) && is_array($controller->pSetup
['includeCSSLibs.'])) {
507 foreach ($controller->pSetup
['includeCSSLibs.'] as $key => $CSSfile) {
508 if (!is_array($CSSfile)) {
509 $cssFileConfig = &$controller->pSetup
['includeCSSLibs.'][$key . '.'];
510 if (isset($cssFileConfig['if.']) && !$controller->cObj
->checkIf($cssFileConfig['if.'])) {
513 if ($cssFileConfig['external']) {
517 $ss = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($CSSfile);
518 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
523 if ($cssFileConfig['import']) {
524 if (!$cssFileConfig['external'] && $ss[0] !== '/') {
525 // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende)
526 $ss = GeneralUtility
::dirname(GeneralUtility
::getIndpEnv('SCRIPT_NAME')) . '/' . $ss;
528 $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), (bool)$cssFileConfig['forceOnTop']);
530 $pageRenderer->addCssLibrary(
532 $cssFileConfig['alternate'] ?
'alternate stylesheet' : 'stylesheet',
533 $cssFileConfig['media'] ?
: 'all',
534 $cssFileConfig['title'] ?
: '',
535 $cssFileConfig['external'] ?
false : empty($cssFileConfig['disableCompression']),
536 (bool)$cssFileConfig['forceOnTop'],
537 $cssFileConfig['allWrap'],
538 (bool)$cssFileConfig['excludeFromConcatenation'] ||
(bool)$cssFileConfig['inline'],
539 $cssFileConfig['allWrap.']['splitChar'],
540 $cssFileConfig['inline']
542 unset($cssFileConfig);
549 // CSS_inlineStyle from TS
550 $style = trim($controller->pSetup
['CSS_inlineStyle'] ??
'');
551 $style .= $controller->cObj
->cObjGet($controller->pSetup
['cssInline.'] ??
null, 'cssInline.');
553 $this->addCssToPageRenderer($controller, $style, true, 'additionalTSFEInlineStyle');
555 // Javascript Libraries
556 if (isset($controller->pSetup
['javascriptLibs.']) && is_array($controller->pSetup
['javascriptLibs.'])) {
557 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, the setting page.javascriptLibs has been deprecated and will be removed in TYPO3 v10.0.
558 trigger_error('The setting page.javascriptLibs will be removed in TYPO3 v10.0.', E_USER_DEPRECATED
);
560 // Include jQuery into the page renderer
561 if (!empty($controller->pSetup
['javascriptLibs.']['jQuery'])) {
562 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, the setting page.javascriptLibs.jQuery has been deprecated and will be removed in TYPO3 v10.0.
563 trigger_error('The setting page.javascriptLibs.jQuery will be removed in TYPO3 v10.0.', E_USER_DEPRECATED
);
565 $jQueryTS = $controller->pSetup
['javascriptLibs.']['jQuery.'];
566 // Check if version / source is set, if not set variable to "NULL" to use the default of the page renderer
567 $version = $jQueryTS['version'] ??
null;
568 $source = $jQueryTS['source'] ??
null;
569 // When "noConflict" is not set or "1" enable the default jQuery noConflict mode, otherwise disable the namespace
570 if (!isset($jQueryTS['noConflict']) ||
!empty($jQueryTS['noConflict'])) {
571 $namespace = 'noConflict';
573 $namespace = PageRenderer
::JQUERY_NAMESPACE_NONE
;
575 $pageRenderer->loadJquery($version, $source, $namespace, true);
578 // JavaScript library files
579 if (isset($controller->pSetup
['includeJSLibs.']) && is_array($controller->pSetup
['includeJSLibs.'])) {
580 foreach ($controller->pSetup
['includeJSLibs.'] as $key => $JSfile) {
581 if (!is_array($JSfile)) {
582 if (isset($controller->pSetup
['includeJSLibs.'][$key . '.']['if.']) && !$controller->cObj
->checkIf($controller->pSetup
['includeJSLibs.'][$key . '.']['if.'])) {
585 if ($controller->pSetup
['includeJSLibs.'][$key . '.']['external']) {
589 $ss = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($JSfile);
590 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
595 $jsFileConfig = &$controller->pSetup
['includeJSLibs.'][$key . '.'];
596 $type = $jsFileConfig['type'];
598 $type = 'text/javascript';
600 $crossOrigin = $jsFileConfig['crossorigin'];
601 if (!$crossOrigin && $jsFileConfig['integrity'] && $jsFileConfig['external']) {
602 $crossOrigin = 'anonymous';
604 $pageRenderer->addJsLibrary(
608 $jsFileConfig['external'] ?
false : empty($jsFileConfig['disableCompression']),
609 (bool)$jsFileConfig['forceOnTop'],
610 $jsFileConfig['allWrap'],
611 (bool)$jsFileConfig['excludeFromConcatenation'],
612 $jsFileConfig['allWrap.']['splitChar'],
613 (bool)$jsFileConfig['async'],
614 $jsFileConfig['integrity'],
615 (bool)$jsFileConfig['defer'],
618 unset($jsFileConfig);
623 if (isset($controller->pSetup
['includeJSFooterlibs.']) && is_array($controller->pSetup
['includeJSFooterlibs.'])) {
624 foreach ($controller->pSetup
['includeJSFooterlibs.'] as $key => $JSfile) {
625 if (!is_array($JSfile)) {
626 if (isset($controller->pSetup
['includeJSFooterlibs.'][$key . '.']['if.']) && !$controller->cObj
->checkIf($controller->pSetup
['includeJSFooterlibs.'][$key . '.']['if.'])) {
629 if ($controller->pSetup
['includeJSFooterlibs.'][$key . '.']['external']) {
633 $ss = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($JSfile);
634 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
639 $jsFileConfig = &$controller->pSetup
['includeJSFooterlibs.'][$key . '.'];
640 $type = $jsFileConfig['type'];
642 $type = 'text/javascript';
644 $crossorigin = $jsFileConfig['crossorigin'];
645 if (!$crossorigin && $jsFileConfig['integrity'] && $jsFileConfig['external']) {
646 $crossorigin = 'anonymous';
648 $pageRenderer->addJsFooterLibrary(
652 $jsFileConfig['external'] ?
false : empty($jsFileConfig['disableCompression']),
653 (bool)$jsFileConfig['forceOnTop'],
654 $jsFileConfig['allWrap'],
655 (bool)$jsFileConfig['excludeFromConcatenation'],
656 $jsFileConfig['allWrap.']['splitChar'],
657 (bool)$jsFileConfig['async'],
658 $jsFileConfig['integrity'],
659 (bool)$jsFileConfig['defer'],
662 unset($jsFileConfig);
668 if (isset($controller->pSetup
['includeJS.']) && is_array($controller->pSetup
['includeJS.'])) {
669 foreach ($controller->pSetup
['includeJS.'] as $key => $JSfile) {
670 if (!is_array($JSfile)) {
671 if (isset($controller->pSetup
['includeJS.'][$key . '.']['if.']) && !$controller->cObj
->checkIf($controller->pSetup
['includeJS.'][$key . '.']['if.'])) {
674 if ($controller->pSetup
['includeJS.'][$key . '.']['external']) {
678 $ss = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($JSfile);
679 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
684 $jsConfig = &$controller->pSetup
['includeJS.'][$key . '.'];
685 $type = $jsConfig['type'];
687 $type = 'text/javascript';
689 $crossorigin = $jsConfig['crossorigin'];
690 if (!$crossorigin && $jsConfig['integrity'] && $jsConfig['external']) {
691 $crossorigin = 'anonymous';
693 $pageRenderer->addJsFile(
696 $jsConfig['external'] ?
false : empty($jsConfig['disableCompression']),
697 (bool)$jsConfig['forceOnTop'],
698 $jsConfig['allWrap'],
699 (bool)$jsConfig['excludeFromConcatenation'],
700 $jsConfig['allWrap.']['splitChar'],
701 (bool)$jsConfig['async'],
702 $jsConfig['integrity'],
703 (bool)$jsConfig['defer'],
711 if (isset($controller->pSetup
['includeJSFooter.']) && is_array($controller->pSetup
['includeJSFooter.'])) {
712 foreach ($controller->pSetup
['includeJSFooter.'] as $key => $JSfile) {
713 if (!is_array($JSfile)) {
714 if (isset($controller->pSetup
['includeJSFooter.'][$key . '.']['if.']) && !$controller->cObj
->checkIf($controller->pSetup
['includeJSFooter.'][$key . '.']['if.'])) {
717 if ($controller->pSetup
['includeJSFooter.'][$key . '.']['external']) {
721 $ss = GeneralUtility
::makeInstance(FilePathSanitizer
::class)->sanitize($JSfile);
722 } catch (\TYPO3\CMS\Core\
Resource\Exception
$e) {
727 $jsConfig = &$controller->pSetup
['includeJSFooter.'][$key . '.'];
728 $type = $jsConfig['type'];
730 $type = 'text/javascript';
732 $crossorigin = $jsConfig['crossorigin'];
733 if (!$crossorigin && $jsConfig['integrity'] && $jsConfig['external']) {
734 $crossorigin = 'anonymous';
736 $pageRenderer->addJsFooterFile(
739 $jsConfig['external'] ?
false : empty($jsConfig['disableCompression']),
740 (bool)$jsConfig['forceOnTop'],
741 $jsConfig['allWrap'],
742 (bool)$jsConfig['excludeFromConcatenation'],
743 $jsConfig['allWrap.']['splitChar'],
744 (bool)$jsConfig['async'],
745 $jsConfig['integrity'],
746 (bool)$jsConfig['defer'],
755 if (isset($controller->pSetup
['headerData.']) && is_array($controller->pSetup
['headerData.'])) {
756 $pageRenderer->addHeaderData($controller->cObj
->cObjGet($controller->pSetup
['headerData.'], 'headerData.'));
759 if (isset($controller->pSetup
['footerData.']) && is_array($controller->pSetup
['footerData.'])) {
760 $pageRenderer->addFooterData($controller->cObj
->cObjGet($controller->pSetup
['footerData.'], 'footerData.'));
762 $controller->generatePageTitle();
764 // @internal hook for EXT:seo, will be gone soon, do not use it in your own extensions
765 $_params = ['page' => $controller->page
];
767 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Frontend\Page\PageGenerator']['generateMetaTags'] ??
[] as $_funcRef) {
768 GeneralUtility
::callUserFunction($_funcRef, $_params, $_ref);
771 $this->generateMetaTagHtml(
772 $controller->pSetup
['meta.'] ??
[],
776 unset($controller->additionalHeaderData
['JSCode']);
777 if (isset($controller->config
['INTincScript']) && is_array($controller->config
['INTincScript'])) {
778 $controller->additionalHeaderData
['JSCode'] = $controller->JSCode
;
779 // Storing the JSCode vars...
780 $controller->config
['INTincScript_ext']['divKey'] = $controller->uniqueHash();
781 $controller->config
['INTincScript_ext']['additionalHeaderData'] = $controller->additionalHeaderData
;
782 // Storing the header-data array
783 $controller->config
['INTincScript_ext']['additionalFooterData'] = $controller->additionalFooterData
;
784 // Storing the footer-data array
785 $controller->config
['INTincScript_ext']['additionalJavaScript'] = $controller->additionalJavaScript
;
786 // Storing the JS-data array
787 $controller->config
['INTincScript_ext']['additionalCSS'] = $controller->additionalCSS
;
788 // Storing the Style-data array
789 $controller->additionalHeaderData
= ['<!--HD_' . $controller->config
['INTincScript_ext']['divKey'] . '-->'];
790 // Clearing the array
791 $controller->additionalFooterData
= ['<!--FD_' . $controller->config
['INTincScript_ext']['divKey'] . '-->'];
792 // Clearing the array
793 $controller->divSection
.= '<!--TDS_' . $controller->config
['INTincScript_ext']['divKey'] . '-->';
795 $controller->INTincScript_loadJSCode();
799 if ($controller->spamProtectEmailAddresses
&& $controller->spamProtectEmailAddresses
!== 'ascii') {
801 // decrypt helper function
802 function decryptCharcode(n,start,end,offset) {
804 if (offset > 0 && n > end) {
805 n = start + (n - end - 1);
806 } else if (offset < 0 && n < start) {
807 n = end - (start - n - 1);
809 return String.fromCharCode(n);
812 function decryptString(enc,offset) {
814 var len = enc.length;
815 for(var i=0; i < len; i++) {
816 var n = enc.charCodeAt(i);
817 if (n >= 0x2B && n <= 0x3A) {
818 dec += decryptCharcode(n,0x2B,0x3A,offset); // 0-9 . , - + / :
819 } else if (n >= 0x40 && n <= 0x5A) {
820 dec += decryptCharcode(n,0x40,0x5A,offset); // A-Z @
821 } else if (n >= 0x61 && n <= 0x7A) {
822 dec += decryptCharcode(n,0x61,0x7A,offset); // a-z
824 dec += enc.charAt(i);
829 // decrypt spam-protected emails
830 function linkTo_UnCryptMailto(s) {
831 location.href = decryptString(s,' . $controller->spamProtectEmailAddresses
* -1 . ');
838 if (is_array($controller->inlineJS
)) {
839 foreach ($controller->inlineJS
as $key => $val) {
840 if (!is_array($val)) {
841 $inlineJS .= LF
. $val . LF
;
845 // defined in TS with page.inlineJS
846 // Javascript inline code
847 $inline = $controller->cObj
->cObjGet($controller->pSetup
['jsInline.'] ??
null, 'jsInline.');
849 $inlineJS .= LF
. $inline . LF
;
851 // Javascript inline code for Footer
852 $inlineFooterJs = $controller->cObj
->cObjGet($controller->pSetup
['jsFooterInline.'] ??
null, 'jsFooterInline.');
854 if ($controller->config
['config']['compressJs'] ??
false) {
855 $pageRenderer->enableCompressJavascript();
856 $minifyErrorScript = ($minifyErrorInline = '');
857 $scriptJsCode = GeneralUtility
::minifyJavaScript($scriptJsCode, $minifyErrorScript);
858 if ($minifyErrorScript) {
859 $this->timeTracker
->setTSlogMessage($minifyErrorScript, 3);
862 $inlineJS = GeneralUtility
::minifyJavaScript($inlineJS, $minifyErrorInline);
863 if ($minifyErrorInline) {
864 $this->timeTracker
->setTSlogMessage($minifyErrorInline, 3);
867 if ($inlineFooterJs) {
868 $inlineFooterJs = GeneralUtility
::minifyJavaScript($inlineFooterJs, $minifyErrorInline);
869 if ($minifyErrorInline) {
870 $this->timeTracker
->setTSlogMessage($minifyErrorInline, 3);
874 if (!isset($controller->config
['config']['removeDefaultJS']) ||
!$controller->config
['config']['removeDefaultJS']) {
875 // include default and inlineJS
877 $pageRenderer->addJsInlineCode('_scriptCode', $scriptJsCode, $controller->config
['config']['compressJs']);
880 $pageRenderer->addJsInlineCode('TS_inlineJS', $inlineJS, $controller->config
['config']['compressJs']);
882 if ($inlineFooterJs) {
883 $pageRenderer->addJsFooterInlineCode('TS_inlineFooter', $inlineFooterJs, $controller->config
['config']['compressJs']);
885 } elseif ($controller->config
['config']['removeDefaultJS'] === 'external') {
887 * This keeps inlineJS from *_INT Objects from being moved to external files.
888 * At this point in frontend rendering *_INT Objects only have placeholders instead
889 * of actual content so moving these placeholders to external files would
890 * a) break the JS file (syntax errors due to the placeholders)
891 * b) the needed JS would never get included to the page
892 * Therefore inlineJS from *_INT Objects must not be moved to external files but
896 $this->stripIntObjectPlaceholder($inlineJS, $inlineJSint);
898 $pageRenderer->addJsInlineCode('TS_inlineJSint', $inlineJSint, $controller->config
['config']['compressJs']);
900 if (trim($scriptJsCode . $inlineJS)) {
901 $pageRenderer->addJsFile(GeneralUtility
::writeJavaScriptContentToTemporaryFile($scriptJsCode . $inlineJS), 'text/javascript', $controller->config
['config']['compressJs']);
903 if ($inlineFooterJs) {
904 $inlineFooterJSint = '';
905 $this->stripIntObjectPlaceholder($inlineFooterJs, $inlineFooterJSint);
906 if ($inlineFooterJSint) {
907 $pageRenderer->addJsFooterInlineCode('TS_inlineFooterJSint', $inlineFooterJSint, $controller->config
['config']['compressJs']);
909 $pageRenderer->addJsFooterFile(GeneralUtility
::writeJavaScriptContentToTemporaryFile($inlineFooterJs), 'text/javascript', $controller->config
['config']['compressJs']);
912 // Include only inlineJS
914 $pageRenderer->addJsInlineCode('TS_inlineJS', $inlineJS, $controller->config
['config']['compressJs']);
916 if ($inlineFooterJs) {
917 $pageRenderer->addJsFooterInlineCode('TS_inlineFooter', $inlineFooterJs, $controller->config
['config']['compressJs']);
920 if (isset($controller->pSetup
['inlineLanguageLabelFiles.']) && is_array($controller->pSetup
['inlineLanguageLabelFiles.'])) {
921 foreach ($controller->pSetup
['inlineLanguageLabelFiles.'] as $key => $languageFile) {
922 if (is_array($languageFile)) {
925 $languageFileConfig = &$controller->pSetup
['inlineLanguageLabelFiles.'][$key . '.'];
926 if (isset($languageFileConfig['if.']) && !$controller->cObj
->checkIf($languageFileConfig['if.'])) {
929 $pageRenderer->addInlineLanguageLabelFile(
931 $languageFileConfig['selectionPrefix'] ?
: '',
932 $languageFileConfig['stripFromSelectionName'] ?
: ''
936 if (isset($controller->pSetup
['inlineSettings.']) && is_array($controller->pSetup
['inlineSettings.'])) {
937 $pageRenderer->addInlineSettingArray('TS', $controller->pSetup
['inlineSettings.']);
939 // Compression and concatenate settings
940 if ($controller->config
['config']['compressCss'] ??
false) {
941 $pageRenderer->enableCompressCss();
943 if ($controller->config
['config']['compressJs'] ??
false) {
944 $pageRenderer->enableCompressJavascript();
946 if ($controller->config
['config']['concatenateCss'] ??
false) {
947 $pageRenderer->enableConcatenateCss();
949 if ($controller->config
['config']['concatenateJs'] ??
false) {
950 $pageRenderer->enableConcatenateJavascript();
952 // Backward compatibility for old configuration
953 // @deprecated - remove this option in TYPO3 v10.0.
954 if ($controller->config
['config']['concatenateJsAndCss'] ??
false) {
955 trigger_error('Setting config.concatenateJsAndCss is deprecated in favor of config.concatenateJs and config.concatenateCss, and will have no effect anymore in TYPO3 v10.0.', E_USER_DEPRECATED
);
956 $pageRenderer->enableConcatenateCss();
957 $pageRenderer->enableConcatenateJavascript();
959 // Add header data block
960 if ($controller->additionalHeaderData
) {
961 $pageRenderer->addHeaderData(implode(LF
, $controller->additionalHeaderData
));
963 // Add footer data block
964 if ($controller->additionalFooterData
) {
965 $pageRenderer->addFooterData(implode(LF
, $controller->additionalFooterData
));
967 // Header complete, now add content
969 if ($controller->config
['config']['disableBodyTag'] ??
false) {
972 $defBT = (isset($controller->pSetup
['bodyTagCObject']) && $controller->pSetup
['bodyTagCObject'])
973 ?
$controller->cObj
->cObjGetSingle($controller->pSetup
['bodyTagCObject'], $controller->pSetup
['bodyTagCObject.'], 'bodyTagCObject')
975 $bodyTag = (isset($controller->pSetup
['bodyTag']) && $controller->pSetup
['bodyTag'])
976 ?
$controller->pSetup
['bodyTag']
978 if (trim($controller->pSetup
['bodyTagAdd'] ??
'')) {
979 $bodyTag = preg_replace('/>$/', '', trim($bodyTag)) . ' ' . trim($controller->pSetup
['bodyTagAdd']) . '>';
982 $pageRenderer->addBodyContent(LF
. $bodyTag);
984 if ($controller->divSection
) {
985 $pageRenderer->addBodyContent(LF
. $controller->divSection
);
988 $pageRenderer->addBodyContent(LF
. $pageContent);
989 if (!empty($controller->config
['INTincScript']) && is_array($controller->config
['INTincScript'])) {
990 // Store the serialized pageRenderer in configuration
991 $controller->config
['INTincScript_ext']['pageRenderer'] = serialize($pageRenderer);
992 // Render complete page, keep placeholders for JavaScript and CSS
993 $pageContent = $pageRenderer->renderPageWithUncachedObjects($controller->config
['INTincScript_ext']['divKey']);
995 // Render complete page
996 $pageContent = $pageRenderer->render();
998 return $pageContent ?
: '';
1001 /*************************
1005 *************************/
1008 * Searches for placeholder created from *_INT cObjects, removes them from
1009 * $searchString and merges them to $intObjects
1011 * @param string $searchString The String which should be cleaned from int-object markers
1012 * @param string $intObjects The String the found int-placeholders are moved to (for further processing)
1014 protected function stripIntObjectPlaceholder(&$searchString, &$intObjects)
1017 preg_match_all('/\\<\\!--INT_SCRIPT.[a-z0-9]*--\\>/', $searchString, $tempArray);
1018 $searchString = preg_replace('/\\<\\!--INT_SCRIPT.[a-z0-9]*--\\>/', '', $searchString);
1019 $intObjects = implode('', $tempArray[0]);
1023 * Generate meta tags from meta tag TypoScript
1025 * @param array $metaTagTypoScript TypoScript configuration for meta tags (e.g. $GLOBALS['TSFE']->pSetup['meta.'])
1026 * @param ContentObjectRenderer $cObj
1028 protected function generateMetaTagHtml(array $metaTagTypoScript, ContentObjectRenderer
$cObj)
1030 $pageRenderer = $this->getPageRenderer();
1032 $typoScriptService = GeneralUtility
::makeInstance(TypoScriptService
::class);
1033 $conf = $typoScriptService->convertTypoScriptArrayToPlainArray($metaTagTypoScript);
1034 foreach ($conf as $key => $properties) {
1036 if (is_array($properties)) {
1037 $nodeValue = $properties['_typoScriptNodeValue'] ??
'';
1038 $value = trim($cObj->stdWrap($nodeValue, $metaTagTypoScript[$key . '.']) ??
'');
1039 if ($value === '' && !empty($properties['value'])) {
1040 $value = $properties['value'];
1044 $value = $properties;
1047 $attribute = 'name';
1048 if ((is_array($properties) && !empty($properties['httpEquivalent'])) ||
strtolower($key) === 'refresh') {
1049 $attribute = 'http-equiv';
1051 if (is_array($properties) && !empty($properties['attribute'])) {
1052 $attribute = $properties['attribute'];
1054 if (is_array($properties) && !empty($properties['replace'])) {
1058 if (!is_array($value)) {
1059 $value = (array)$value;
1061 foreach ($value as $subValue) {
1062 if (trim($subValue ??
'') !== '') {
1063 $pageRenderer->setMetaTag($attribute, $key, $subValue, [], $replace);
1070 * @return PageRenderer
1072 protected function getPageRenderer(): PageRenderer
1074 return GeneralUtility
::makeInstance(PageRenderer
::class);
1078 * Adds inline CSS code, by respecting the inlineStyle2TempFile option
1080 * @param TypoScriptFrontendController $controller
1081 * @param string $cssStyles the inline CSS styling
1082 * @param bool $excludeFromConcatenation option to see if it should be concatenated
1083 * @param string $inlineBlockName the block name to add it
1085 protected function addCssToPageRenderer(TypoScriptFrontendController
$controller, string $cssStyles, bool $excludeFromConcatenation, string $inlineBlockName)
1087 if (empty($controller->config
['config']['inlineStyle2TempFile'] ??
false)) {
1088 $this->getPageRenderer()->addCssInlineBlock($inlineBlockName, $cssStyles, !empty($controller->config
['config']['compressCss'] ??
false));
1090 $this->getPageRenderer()->addCssFile(
1091 GeneralUtility
::writeStyleSheetContentToTemporaryFile($cssStyles),
1095 (bool)($controller->config
['config']['compressCss'] ??
false),
1098 $excludeFromConcatenation
1104 * Generates the <html> tag by evaluting TypoScript configuration, usually found via:
1106 * - Adding extra attributes in addition to pre-generated ones (e.g. "dir")
1107 * config.htmlTag.attributes.no-js = 1
1108 * config.htmlTag.attributes.empty-attribute =
1110 * - Adding one full string (no stdWrap!) to the "<html $htmlTagAttributes {config.htmlTag_setParams}>" tag
1111 * config.htmlTag_setParams = string|"none"
1113 * If config.htmlTag_setParams = none is set, even the pre-generated values are not added at all anymore.
1115 * - "config.htmlTag_stdWrap" always applies over the whole compiled tag.
1117 * @param array $htmlTagAttributes pre-generated attributes by doctype/direction etc. values.
1118 * @param array $configuration the TypoScript configuration "config." array
1119 * @param ContentObjectRenderer $cObj
1120 * @return string the full <html> tag as string
1122 protected function generateHtmlTag(array $htmlTagAttributes, array $configuration, ContentObjectRenderer
$cObj): string
1124 if (is_array($configuration['htmlTag.']['attributes.'] ??
null)) {
1125 $attributeString = '';
1126 foreach ($configuration['htmlTag.']['attributes.'] as $attributeName => $value) {
1127 $attributeString .= ' ' . htmlspecialchars($attributeName) . ($value !== '' ?
'="' . htmlspecialchars((string)$value) . '"' : '');
1128 // If e.g. "htmlTag.attributes.dir" is set, make sure it is not added again with "implodeAttributes()"
1129 if (isset($htmlTagAttributes[$attributeName])) {
1130 unset($htmlTagAttributes[$attributeName]);
1133 $attributeString = ltrim(GeneralUtility
::implodeAttributes($htmlTagAttributes) . $attributeString);
1134 } elseif (($configuration['htmlTag_setParams'] ??
'') === 'none') {
1135 $attributeString = '';
1136 } elseif (isset($configuration['htmlTag_setParams'])) {
1137 $attributeString = $configuration['htmlTag_setParams'];
1139 $attributeString = GeneralUtility
::implodeAttributes($htmlTagAttributes);
1141 $htmlTag = '<html' . ($attributeString ?
' ' . $attributeString : '') . '>';
1142 if (isset($configuration['htmlTag_stdWrap.'])) {
1143 $htmlTag = $cObj->stdWrap($htmlTag, $configuration['htmlTag_stdWrap.']);
1149 * This request handler can handle any frontend request.
1151 * @param ServerRequestInterface $request
1152 * @return bool If the request is not an eID request, TRUE otherwise FALSE
1154 public function canHandleRequest(ServerRequestInterface
$request): bool
1160 * Returns the priority - how eager the handler is to actually handle the
1163 * @return int The priority of the request handler.
1165 public function getPriority(): int