d11a1d6c5eecb40eee53b0f8e807687209b151ed
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Http / RequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
18 use TYPO3\CMS\Core\Core\Bootstrap;
19 use TYPO3\CMS\Core\Core\RequestHandlerInterface;
20 use TYPO3\CMS\Core\FrontendEditing\FrontendEditingController;
21 use TYPO3\CMS\Core\TimeTracker\NullTimeTracker;
22 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\MathUtility;
25 use TYPO3\CMS\Core\Utility\MonitorUtility;
26 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
27 use TYPO3\CMS\Frontend\Page\PageGenerator;
28 use TYPO3\CMS\Frontend\Utility\CompressionUtility;
29 use TYPO3\CMS\Frontend\View\AdminPanelView;
30
31 /**
32 * This is the main entry point of the TypoScript driven standard front-end
33 *
34 * Basically put, this is the script which all requests for TYPO3 delivered pages goes to in the
35 * frontend (the website). The script instantiates a $TSFE object, includes libraries and does a little logic here
36 * and there in order to instantiate the right classes to create the webpage.
37 * Previously, this was called index_ts.php and also included the logic for the lightweight "eID" concept,
38 * which is now handled in a separate request handler (EidRequestHandler).
39 */
40 class RequestHandler implements RequestHandlerInterface {
41
42 /**
43 * Instance of the current TYPO3 bootstrap
44 * @var Bootstrap
45 */
46 protected $bootstrap;
47
48 /**
49 * Instance of the timetracker
50 * @var NullTimeTracker|TimeTracker
51 */
52 protected $timeTracker;
53
54 /**
55 * Instance of the TSFE object
56 * @var TypoScriptFrontendController
57 */
58 protected $controller;
59
60 /**
61 * Constructor handing over the bootstrap
62 *
63 * @param Bootstrap $bootstrap
64 */
65 public function __construct(Bootstrap $bootstrap) {
66 $this->bootstrap = $bootstrap;
67 }
68
69 /**
70 * Handles a frontend request
71 *
72 * @return void
73 */
74 public function handleRequest() {
75 $this->initializeTimeTracker();
76
77 // Hook to preprocess the current request:
78 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preprocessRequest'])) {
79 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preprocessRequest'] as $hookFunction) {
80 $hookParameters = array();
81 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $hookParameters);
82 }
83 unset($hookFunction);
84 unset($hookParameters);
85 }
86
87 $this->initializeController();
88
89 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_force']
90 && !GeneralUtility::cmpIP(
91 GeneralUtility::getIndpEnv('REMOTE_ADDR'),
92 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'])
93 ) {
94 $this->controller->pageUnavailableAndExit('This page is temporarily unavailable.');
95 }
96
97 $this->controller->connectToDB();
98 $this->controller->sendRedirect();
99
100 // Output compression
101 // Remove any output produced until now
102 $this->bootstrap->endOutputBufferingAndCleanPreviousOutput();
103 $this->initializeOutputCompression();
104
105 // Initializing the Frontend User
106 $this->timeTracker->push('Front End user initialized', '');
107 $this->controller->initFEuser();
108 $this->timeTracker->pull();
109
110 // Initializing a possible logged-in Backend User
111 /** @var $GLOBALS['BE_USER'] \TYPO3\CMS\Backend\FrontendBackendUserAuthentication */
112 $GLOBALS['BE_USER'] = $this->controller->initializeBackendUser();
113
114 // Process the ID, type and other parameters.
115 // After this point we have an array, $page in TSFE, which is the page-record
116 // of the current page, $id.
117 $this->timeTracker->push('Process ID', '');
118 // Initialize admin panel since simulation settings are required here:
119 if ($this->controller->isBackendUserLoggedIn()) {
120 $GLOBALS['BE_USER']->initializeAdminPanel();
121 $this->bootstrap->loadExtensionTables(TRUE);
122 } else {
123 $this->bootstrap->loadCachedTca();
124 }
125 $this->controller->checkAlternativeIdMethods();
126 $this->controller->clear_preview();
127 $this->controller->determineId();
128
129 // Now, if there is a backend user logged in and he has NO access to this page,
130 // then re-evaluate the id shown! _GP('ADMCMD_noBeUser') is placed here because
131 // \TYPO3\CMS\Version\Hook\PreviewHook might need to know if a backend user is logged in.
132 if (
133 $this->controller->isBackendUserLoggedIn()
134 && (!$GLOBALS['BE_USER']->extPageReadAccess($this->controller->page) || GeneralUtility::_GP('ADMCMD_noBeUser'))
135 ) {
136 // Remove user
137 unset($GLOBALS['BE_USER']);
138 $this->controller->beUserLogin = FALSE;
139 // Re-evaluate the page-id.
140 $this->controller->checkAlternativeIdMethods();
141 $this->controller->clear_preview();
142 $this->controller->determineId();
143 }
144
145 $this->controller->makeCacheHash();
146 $this->timeTracker->pull();
147
148 // Admin Panel & Frontend editing
149 if ($this->controller->isBackendUserLoggedIn()) {
150 $GLOBALS['BE_USER']->initializeFrontendEdit();
151 if ($GLOBALS['BE_USER']->adminPanel instanceof AdminPanelView) {
152 $this->bootstrap
153 ->initializeLanguageObject()
154 ->initializeSpriteManager();
155 }
156 if ($GLOBALS['BE_USER']->frontendEdit instanceof FrontendEditingController) {
157 $GLOBALS['BE_USER']->frontendEdit->initConfigOptions();
158 }
159 }
160
161 // Starts the template
162 $this->timeTracker->push('Start Template', '');
163 $this->controller->initTemplate();
164 $this->timeTracker->pull();
165 // Get from cache
166 $this->timeTracker->push('Get Page from cache', '');
167 $this->controller->getFromCache();
168 $this->timeTracker->pull();
169 // Get config if not already gotten
170 // After this, we should have a valid config-array ready
171 $this->controller->getConfigArray();
172 // Setting language and locale
173 $this->timeTracker->push('Setting language and locale', '');
174 $this->controller->settingLanguage();
175 $this->controller->settingLocale();
176 $this->timeTracker->pull();
177
178 // Convert POST data to internal "renderCharset" if different from the metaCharset
179 $this->controller->convPOSTCharset();
180
181 // Check JumpUrl
182 $this->controller->setExternalJumpUrl();
183
184 $this->controller->handleDataSubmission();
185
186 // Check for shortcut page and redirect
187 $this->controller->checkPageForShortcutRedirect();
188 $this->controller->checkPageForMountpointRedirect();
189
190 // Generate page
191 $this->controller->setUrlIdToken();
192 $this->timeTracker->push('Page generation', '');
193 if ($this->controller->isGeneratePage()) {
194 $this->controller->generatePage_preProcessing();
195 $temp_theScript = $this->controller->generatePage_whichScript();
196 if ($temp_theScript) {
197 include $temp_theScript;
198 } else {
199 PageGenerator::pagegenInit();
200 // Global content object
201 $this->controller->newCObj();
202 // Content generation
203 if (!$this->controller->isINTincScript()) {
204 PageGenerator::renderContent();
205 $this->controller->setAbsRefPrefix();
206 }
207 }
208 $this->controller->generatePage_postProcessing();
209 } elseif ($this->controller->isINTincScript()) {
210 PageGenerator::pagegenInit();
211 // Global content object
212 $this->controller->newCObj();
213 }
214 $this->controller->releaseLocks();
215 $this->timeTracker->pull();
216
217 // Render non-cached parts
218 if ($this->controller->isINTincScript()) {
219 $this->timeTracker->push('Non-cached objects', '');
220 $this->controller->INTincScript();
221 $this->timeTracker->pull();
222 }
223
224 // Output content
225 $sendTSFEContent = FALSE;
226 if ($this->controller->isOutputting()) {
227 $this->timeTracker->push('Print Content', '');
228 $this->controller->processOutput();
229 $sendTSFEContent = TRUE;
230 $this->timeTracker->pull();
231 }
232 // Store session data for fe_users
233 $this->controller->storeSessionData();
234 // Statistics
235 $GLOBALS['TYPO3_MISC']['microtime_end'] = microtime(TRUE);
236 $this->controller->setParseTime();
237 if (isset($this->controller->config['config']['debug'])) {
238 $debugParseTime = (bool)$this->controller->config['config']['debug'];
239 } else {
240 $debugParseTime = !empty($this->controller->TYPO3_CONF_VARS['FE']['debug']);
241 }
242 if ($this->controller->isOutputting() && $debugParseTime) {
243 $this->controller->content .= LF . '<!-- Parsetime: ' . $this->controller->scriptParseTime . 'ms -->';
244 }
245 // Check JumpUrl
246 $this->controller->jumpurl();
247 // Preview info
248 $this->controller->previewInfo();
249 // Hook for end-of-frontend
250 $this->controller->hook_eofe();
251 // Finish timetracking
252 $this->timeTracker->pull();
253 // Check memory usage
254 MonitorUtility::peakMemoryUsage();
255 // beLoginLinkIPList
256 echo $this->controller->beLoginLinkIPList();
257
258 // Admin panel
259 if (
260 $this->controller->isBackendUserLoggedIn()
261 && $GLOBALS['BE_USER'] instanceof FrontendBackendUserAuthentication
262 && $GLOBALS['BE_USER']->isAdminPanelVisible()
263 ) {
264 $this->controller->content = str_ireplace('</head>', $GLOBALS['BE_USER']->adminPanel->getAdminPanelHeaderData() . '</head>', $this->controller->content);
265 $this->controller->content = str_ireplace('</body>', $GLOBALS['BE_USER']->displayAdminPanel() . '</body>', $this->controller->content);
266 }
267
268 if ($sendTSFEContent) {
269 echo $this->controller->content;
270 }
271 // Debugging Output
272 if (isset($GLOBALS['error']) && is_object($GLOBALS['error']) && @is_callable(array($GLOBALS['error'], 'debugOutput'))) {
273 $GLOBALS['error']->debugOutput();
274 }
275 if (TYPO3_DLOG) {
276 GeneralUtility::devLog('END of FRONTEND session', 'cms', 0, array('_FLUSH' => TRUE));
277 }
278 }
279
280 /**
281 * This request handler can handle any frontend request.
282 *
283 * @return bool If the request is not an eID request, TRUE otherwise FALSE
284 */
285 public function canHandleRequest() {
286 return GeneralUtility::_GP('eID') ? FALSE : TRUE;
287 }
288
289 /**
290 * Returns the priority - how eager the handler is to actually handle the
291 * request.
292 *
293 * @return int The priority of the request handler.
294 */
295 public function getPriority() {
296 return 50;
297 }
298
299 /**
300 * Initializes output compression when enabled, could be split up and put into Bootstrap
301 * at a later point
302 */
303 protected function initializeOutputCompression() {
304 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] && extension_loaded('zlib')) {
305 if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'])) {
306 @ini_set('zlib.output_compression_level', $GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel']);
307 }
308 ob_start(array(GeneralUtility::makeInstance(CompressionUtility::class), 'compressionOutputHandler'));
309 }
310 }
311
312 /**
313 * Timetracking started depending if a Backend User is logged in
314 *
315 * @return void
316 */
317 protected function initializeTimeTracker() {
318 $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']);
319 if (empty($configuredCookieName)) {
320 $configuredCookieName = 'be_typo_user';
321 }
322 if ($_COOKIE[$configuredCookieName]) {
323 $this->timeTracker = new TimeTracker();
324 } else {
325 $this->timeTracker = new NullTimeTracker();
326 }
327
328 // We have to define this as reference here, because there is code around
329 // which exchanges the TT object in the global variable. The reference ensures
330 // that the $timeTracker member always works on the same object as the global variable.
331 // This is a dirty workaround and bypasses the protected access modifier of the $timeTracker member.
332 $GLOBALS['TT'] = &$this->timeTracker;
333 $this->timeTracker->start();
334 }
335
336 /**
337 * Creates an instance of TSFE and sets it as a global variable
338 *
339 * @return void
340 */
341 protected function initializeController() {
342 $this->controller = GeneralUtility::makeInstance(
343 TypoScriptFrontendController::class,
344 $GLOBALS['TYPO3_CONF_VARS'],
345 GeneralUtility::_GP('id'),
346 GeneralUtility::_GP('type'),
347 GeneralUtility::_GP('no_cache'),
348 GeneralUtility::_GP('cHash'),
349 GeneralUtility::_GP('jumpurl'),
350 GeneralUtility::_GP('MP'),
351 GeneralUtility::_GP('RDCT')
352 );
353 // setting the global variable for the controller
354 // We have to define this as reference here, because there is code around
355 // which exchanges the TSFE object in the global variable. The reference ensures
356 // that the $controller member always works on the same object as the global variable.
357 // This is a dirty workaround and bypasses the protected access modifier of the controller member.
358 $GLOBALS['TSFE'] = &$this->controller;
359 }
360 }