[TASK] Move FE OutputCompression to PSR-15
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Http / RequestHandler.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\Http;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use Psr\Http\Server\RequestHandlerInterface as PsrRequestHandlerInterface;
21 use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
22 use TYPO3\CMS\Core\Core\Bootstrap;
23 use TYPO3\CMS\Core\FrontendEditing\FrontendEditingController;
24 use TYPO3\CMS\Core\Http\NullResponse;
25 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
26 use TYPO3\CMS\Core\Http\Response;
27 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
30 use TYPO3\CMS\Frontend\Page\PageGenerator;
31 use TYPO3\CMS\Frontend\View\AdminPanelView;
32
33 /**
34 * This is the main entry point of the TypoScript driven standard front-end
35 *
36 * Basically put, this is the script which all requests for TYPO3 delivered pages goes to in the
37 * frontend (the website). The script instantiates a $TSFE object, includes libraries and does a little logic here
38 * and there in order to instantiate the right classes to create the webpage.
39 * Previously, this was called index_ts.php and also included the logic for the lightweight "eID" concept,
40 * which is now handled in a separate middleware (EidHandler).
41 */
42 class RequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterface
43 {
44 /**
45 * Instance of the current TYPO3 bootstrap
46 * @var Bootstrap
47 */
48 protected $bootstrap;
49
50 /**
51 * Instance of the timetracker
52 * @var TimeTracker
53 */
54 protected $timeTracker;
55
56 /**
57 * Constructor handing over the bootstrap and the original request
58 *
59 * @param Bootstrap $bootstrap
60 */
61 public function __construct(Bootstrap $bootstrap)
62 {
63 $this->bootstrap = $bootstrap;
64 }
65
66 /**
67 * Handles a frontend request
68 *
69 * @param ServerRequestInterface $request
70 * @return ResponseInterface
71 */
72 public function handleRequest(ServerRequestInterface $request): ResponseInterface
73 {
74 return $this->handle($request);
75 }
76
77 /**
78 * Handles a frontend request, after finishing running middlewares
79 *
80 * @param ServerRequestInterface $request
81 * @return ResponseInterface|null
82 */
83 public function handle(ServerRequestInterface $request): ResponseInterface
84 {
85 // Fetch the initialized time tracker object
86 $this->timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
87 /** @var TypoScriptFrontendController $controller */
88 $controller = $GLOBALS['TSFE'];
89
90 // Initializing the Frontend User
91 $this->timeTracker->push('Front End user initialized', '');
92 $controller->initFEuser();
93 $this->timeTracker->pull();
94
95 // Initializing a possible logged-in Backend User
96 /** @var $GLOBALS['BE_USER'] \TYPO3\CMS\Backend\FrontendBackendUserAuthentication */
97 $GLOBALS['BE_USER'] = $controller->initializeBackendUser();
98
99 // Process the ID, type and other parameters.
100 // After this point we have an array, $page in TSFE, which is the page-record
101 // of the current page, $id.
102 $this->timeTracker->push('Process ID', '');
103 // Initialize admin panel since simulation settings are required here:
104 if ($controller->isBackendUserLoggedIn()) {
105 $GLOBALS['BE_USER']->initializeAdminPanel();
106 $this->bootstrap
107 ->initializeBackendRouter()
108 ->loadExtTables();
109 }
110 $controller->checkAlternativeIdMethods();
111 $controller->clear_preview();
112 $controller->determineId();
113
114 // Now, if there is a backend user logged in and he has NO access to this page,
115 // then re-evaluate the id shown! _GP('ADMCMD_noBeUser') is placed here because
116 // \TYPO3\CMS\Version\Hook\PreviewHook might need to know if a backend user is logged in.
117 if (
118 $controller->isBackendUserLoggedIn()
119 && (!$GLOBALS['BE_USER']->extPageReadAccess($controller->page) || GeneralUtility::_GP('ADMCMD_noBeUser'))
120 ) {
121 // Remove user
122 unset($GLOBALS['BE_USER']);
123 $controller->beUserLogin = false;
124 // Re-evaluate the page-id.
125 $controller->checkAlternativeIdMethods();
126 $controller->clear_preview();
127 $controller->determineId();
128 }
129
130 $controller->makeCacheHash();
131 $this->timeTracker->pull();
132
133 // Admin Panel & Frontend editing
134 if ($controller->isBackendUserLoggedIn()) {
135 $GLOBALS['BE_USER']->initializeFrontendEdit();
136 if ($GLOBALS['BE_USER']->adminPanel instanceof AdminPanelView) {
137 $this->bootstrap->initializeLanguageObject();
138 }
139 if ($GLOBALS['BE_USER']->frontendEdit instanceof FrontendEditingController) {
140 $GLOBALS['BE_USER']->frontendEdit->initConfigOptions();
141 }
142 }
143
144 // Starts the template
145 $this->timeTracker->push('Start Template', '');
146 $controller->initTemplate();
147 $this->timeTracker->pull();
148 // Get from cache
149 $this->timeTracker->push('Get Page from cache', '');
150 $controller->getFromCache();
151 $this->timeTracker->pull();
152 // Get config if not already gotten
153 // After this, we should have a valid config-array ready
154 $controller->getConfigArray();
155 // Setting language and locale
156 $this->timeTracker->push('Setting language and locale', '');
157 $controller->settingLanguage();
158 $controller->settingLocale();
159 $this->timeTracker->pull();
160
161 // Convert POST data to utf-8 for internal processing if metaCharset is different
162 $controller->convPOSTCharset();
163
164 $controller->initializeRedirectUrlHandlers();
165
166 $controller->handleDataSubmission();
167
168 // Check for shortcut page and redirect
169 $controller->checkPageForShortcutRedirect();
170 $controller->checkPageForMountpointRedirect();
171
172 // Generate page
173 $controller->setUrlIdToken();
174 $this->timeTracker->push('Page generation', '');
175 if ($controller->isGeneratePage()) {
176 $controller->generatePage_preProcessing();
177 $controller->preparePageContentGeneration();
178 // Content generation
179 if (!$controller->isINTincScript()) {
180 PageGenerator::renderContent();
181 $controller->setAbsRefPrefix();
182 }
183 $controller->generatePage_postProcessing();
184 } elseif ($controller->isINTincScript()) {
185 $controller->preparePageContentGeneration();
186 }
187 $controller->releaseLocks();
188 $this->timeTracker->pull();
189
190 // Render non-cached parts
191 if ($controller->isINTincScript()) {
192 $this->timeTracker->push('Non-cached objects', '');
193 $controller->INTincScript();
194 $this->timeTracker->pull();
195 }
196
197 // Create a Response object when sending content
198 $response = new Response();
199
200 // Output content
201 $isOutputting = $controller->isOutputting();
202 if ($isOutputting) {
203 $this->timeTracker->push('Print Content', '');
204 $controller->processOutput();
205 $this->timeTracker->pull();
206 }
207 // Store session data for fe_users
208 $controller->storeSessionData();
209
210 $redirectResponse = $controller->redirectToExternalUrl();
211 if ($redirectResponse instanceof ResponseInterface) {
212 return $redirectResponse;
213 }
214
215 // Statistics
216 $GLOBALS['TYPO3_MISC']['microtime_end'] = microtime(true);
217 if ($isOutputting && ($controller->config['config']['debug'] ?? !empty($GLOBALS['TYPO3_CONF_VARS']['FE']['debug']))) {
218 $response = $response->withHeader('X-TYPO3-Parsetime', $this->timeTracker->getParseTime() . 'ms');
219 }
220
221 // Preview info
222 $controller->previewInfo();
223 // Hook for end-of-frontend
224 $controller->hook_eofe();
225 // Finish timetracking
226 $this->timeTracker->pull();
227
228 // Admin panel
229 if ($controller->isBackendUserLoggedIn() && $GLOBALS['BE_USER'] instanceof FrontendBackendUserAuthentication && $GLOBALS['BE_USER']->isAdminPanelVisible()) {
230 $controller->content = str_ireplace('</body>', $GLOBALS['BE_USER']->displayAdminPanel() . '</body>', $controller->content);
231 }
232
233 if ($isOutputting) {
234 $response->getBody()->write($controller->content);
235 }
236
237 return $isOutputting ? $response : new NullResponse();
238 }
239
240 /**
241 * This request handler can handle any frontend request.
242 *
243 * @param ServerRequestInterface $request
244 * @return bool If the request is not an eID request, TRUE otherwise FALSE
245 */
246 public function canHandleRequest(ServerRequestInterface $request): bool
247 {
248 return true;
249 }
250
251 /**
252 * Returns the priority - how eager the handler is to actually handle the
253 * request.
254 *
255 * @return int The priority of the request handler.
256 */
257 public function getPriority(): int
258 {
259 return 50;
260 }
261 }