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