[TASK] Split Frontend Requests logic - part 1
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / RequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Frontend;
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 $this->controller->checkJumpUrlReferer();
184
185 $this->controller->handleDataSubmission();
186
187 // Check for shortcut page and redirect
188 $this->controller->checkPageForShortcutRedirect();
189 $this->controller->checkPageForMountpointRedirect();
190
191 // Generate page
192 $this->controller->setUrlIdToken();
193 $this->timeTracker->push('Page generation', '');
194 if ($this->controller->isGeneratePage()) {
195 $this->controller->generatePage_preProcessing();
196 $temp_theScript = $this->controller->generatePage_whichScript();
197 if ($temp_theScript) {
198 include $temp_theScript;
199 } else {
200 PageGenerator::pagegenInit();
201 // Global content object
202 $this->controller->newCObj();
203 // LIBRARY INCLUSION, TypoScript
204 $temp_incFiles = PageGenerator::getIncFiles();
205 foreach ($temp_incFiles as $temp_file) {
206 include_once './' . $temp_file;
207 }
208 // Content generation
209 if (!$this->controller->isINTincScript()) {
210 PageGenerator::renderContent();
211 $this->controller->setAbsRefPrefix();
212 }
213 }
214 $this->controller->generatePage_postProcessing();
215 } elseif ($this->controller->isINTincScript()) {
216 PageGenerator::pagegenInit();
217 // Global content object
218 $this->controller->newCObj();
219 // LIBRARY INCLUSION, TypoScript
220 $temp_incFiles = PageGenerator::getIncFiles();
221 foreach ($temp_incFiles as $temp_file) {
222 include_once './' . $temp_file;
223 }
224 }
225 $this->timeTracker->pull();
226
227 // Render non-cached parts
228 if ($this->controller->isINTincScript()) {
229 $this->timeTracker->push('Non-cached objects', '');
230 $this->controller->INTincScript();
231 $this->timeTracker->pull();
232 }
233
234 // Output content
235 $sendTSFEContent = FALSE;
236 if ($this->controller->isOutputting()) {
237 $this->timeTracker->push('Print Content', '');
238 $this->controller->processOutput();
239 $sendTSFEContent = TRUE;
240 $this->timeTracker->pull();
241 }
242 // Store session data for fe_users
243 $this->controller->storeSessionData();
244 // Statistics
245 $GLOBALS['TYPO3_MISC']['microtime_end'] = microtime(TRUE);
246 $this->controller->setParseTime();
247 if (isset($this->controller->config['config']['debug'])) {
248 $debugParseTime = (bool)$this->controller->config['config']['debug'];
249 } else {
250 $debugParseTime = !empty($this->controller->TYPO3_CONF_VARS['FE']['debug']);
251 }
252 if ($this->controller->isOutputting() && $debugParseTime) {
253 $this->controller->content .= LF . '<!-- Parsetime: ' . $this->controller->scriptParseTime . 'ms -->';
254 }
255 // Check JumpUrl
256 $this->controller->jumpurl();
257 // Preview info
258 $this->controller->previewInfo();
259 // Hook for end-of-frontend
260 $this->controller->hook_eofe();
261 // Finish timetracking
262 $this->timeTracker->pull();
263 // Check memory usage
264 MonitorUtility::peakMemoryUsage();
265 // beLoginLinkIPList
266 echo $this->controller->beLoginLinkIPList();
267
268 // Admin panel
269 if (
270 $this->controller->isBackendUserLoggedIn()
271 && $GLOBALS['BE_USER'] instanceof FrontendBackendUserAuthentication
272 && $GLOBALS['BE_USER']->isAdminPanelVisible()
273 ) {
274 $this->controller->content = str_ireplace('</head>', $GLOBALS['BE_USER']->adminPanel->getAdminPanelHeaderData() . '</head>', $this->controller->content);
275 $this->controller->content = str_ireplace('</body>', $GLOBALS['BE_USER']->displayAdminPanel() . '</body>', $this->controller->content);
276 }
277
278 if ($sendTSFEContent) {
279 echo $this->controller->content;
280 }
281 // Debugging Output
282 if (isset($GLOBALS['error']) && is_object($GLOBALS['error']) && @is_callable(array($GLOBALS['error'], 'debugOutput'))) {
283 $GLOBALS['error']->debugOutput();
284 }
285 if (TYPO3_DLOG) {
286 GeneralUtility::devLog('END of FRONTEND session', 'cms', 0, array('_FLUSH' => TRUE));
287 }
288 }
289
290 /**
291 * This request handler can handle any frontend request.
292 *
293 * @return bool If the request is not an eID request, TRUE otherwise FALSE
294 */
295 public function canHandleRequest() {
296 return GeneralUtility::_GP('eID') ? FALSE : TRUE;
297 }
298
299 /**
300 * Returns the priority - how eager the handler is to actually handle the
301 * request.
302 *
303 * @return int The priority of the request handler.
304 */
305 public function getPriority() {
306 return 50;
307 }
308
309 /**
310 * Initializes output compression when enabled, could be split up and put into Bootstrap
311 * at a later point
312 */
313 protected function initializeOutputCompression() {
314 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] && extension_loaded('zlib')) {
315 if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'])) {
316 @ini_set('zlib.output_compression_level', $GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel']);
317 }
318 ob_start(array(GeneralUtility::makeInstance(CompressionUtility::class), 'compressionOutputHandler'));
319 }
320 }
321
322 /**
323 * Timetracking started depending if a Backend User is logged in
324 *
325 * @return void
326 */
327 protected function initializeTimeTracker() {
328 $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']);
329 if (empty($configuredCookieName)) {
330 $configuredCookieName = 'be_typo_user';
331 }
332 if ($_COOKIE[$configuredCookieName]) {
333 $this->timeTracker = new TimeTracker();
334 } else {
335 $this->timeTracker = new NullTimeTracker();
336 }
337
338 $GLOBALS['TT'] = $this->timeTracker;
339 $this->timeTracker->start();
340 }
341
342 /**
343 * Creates an instance of TSFE and sets it as a global variable
344 *
345 * @return void
346 */
347 protected function initializeController() {
348 $this->controller = GeneralUtility::makeInstance(
349 TypoScriptFrontendController::class,
350 $GLOBALS['TYPO3_CONF_VARS'],
351 GeneralUtility::_GP('id'),
352 GeneralUtility::_GP('type'),
353 GeneralUtility::_GP('no_cache'),
354 GeneralUtility::_GP('cHash'),
355 GeneralUtility::_GP('jumpurl'),
356 GeneralUtility::_GP('MP'),
357 GeneralUtility::_GP('RDCT')
358 );
359 // setting the global variable for the controller
360 $GLOBALS['TSFE'] = $this->controller;
361 }
362 }