| Total Complexity | 43 | 
| Total Lines | 310 | 
| Duplicated Lines | 2.58 % | 
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like RequestHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RequestHandler, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 39 | class RequestHandler implements RequestHandlerInterface | ||
| 40 | { | ||
| 41 | /** | ||
| 42 | * Instance of the current TYPO3 bootstrap | ||
| 43 | * @var Bootstrap | ||
| 44 | */ | ||
| 45 | protected $bootstrap; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Instance of the timetracker | ||
| 49 | * @var TimeTracker | ||
| 50 | */ | ||
| 51 | protected $timeTracker; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Instance of the TSFE object | ||
| 55 | * @var TypoScriptFrontendController | ||
| 56 | */ | ||
| 57 | protected $controller; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * The request handed over | ||
| 61 | * @var \Psr\Http\Message\ServerRequestInterface | ||
| 62 | */ | ||
| 63 | protected $request; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Constructor handing over the bootstrap and the original request | ||
| 67 | * | ||
| 68 | * @param Bootstrap $bootstrap | ||
| 69 | */ | ||
| 70 | public function __construct(Bootstrap $bootstrap) | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Handles a frontend request | ||
| 77 | * | ||
| 78 | * @param \Psr\Http\Message\ServerRequestInterface $request | ||
| 79 | * @return \Psr\Http\Message\ResponseInterface|null | ||
| 80 | */ | ||
| 81 | public function handleRequest(\Psr\Http\Message\ServerRequestInterface $request) | ||
| 82 |     { | ||
| 83 | $response = null; | ||
| 84 | $this->request = $request; | ||
| 85 | $this->initializeTimeTracker(); | ||
| 86 | |||
| 87 | // Hook to preprocess the current request: | ||
| 88 |         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preprocessRequest'] ?? [] as $hookFunction) { | ||
| 89 | $hookParameters = []; | ||
| 90 | GeneralUtility::callUserFunction($hookFunction, $hookParameters, $hookParameters); | ||
| 91 | } | ||
| 92 | |||
| 93 | $this->initializeController(); | ||
| 94 | |||
| 95 | if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_force'] | ||
| 96 | && !GeneralUtility::cmpIP( | ||
| 97 |                 GeneralUtility::getIndpEnv('REMOTE_ADDR'), | ||
| 98 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] | ||
| 99 | ) | ||
| 100 |         ) { | ||
| 101 |             $this->controller->pageUnavailableAndExit('This page is temporarily unavailable.'); | ||
| 102 | } | ||
| 103 | |||
| 104 | $this->controller->connectToDB(); | ||
| 105 | |||
| 106 | // Output compression | ||
| 107 | // Remove any output produced until now | ||
| 108 | $this->bootstrap->endOutputBufferingAndCleanPreviousOutput(); | ||
| 109 | $this->initializeOutputCompression(); | ||
| 110 | |||
| 111 | // Initializing the Frontend User | ||
| 112 |         $this->timeTracker->push('Front End user initialized', ''); | ||
| 113 | $this->controller->initFEuser(); | ||
| 114 | $this->timeTracker->pull(); | ||
| 115 | |||
| 116 | // Initializing a possible logged-in Backend User | ||
| 117 | /** @var $GLOBALS['BE_USER'] \TYPO3\CMS\Backend\FrontendBackendUserAuthentication */ | ||
| 118 | $GLOBALS['BE_USER'] = $this->controller->initializeBackendUser(); | ||
| 119 | |||
| 120 | // Process the ID, type and other parameters. | ||
| 121 | // After this point we have an array, $page in TSFE, which is the page-record | ||
| 122 | // of the current page, $id. | ||
| 123 |         $this->timeTracker->push('Process ID', ''); | ||
| 124 | // Initialize admin panel since simulation settings are required here: | ||
| 125 |         if ($this->controller->isBackendUserLoggedIn()) { | ||
| 126 | $GLOBALS['BE_USER']->initializeAdminPanel(); | ||
| 127 | $this->bootstrap | ||
| 128 | ->initializeBackendRouter() | ||
| 129 | ->loadExtTables(); | ||
| 130 | } | ||
| 131 | $this->controller->checkAlternativeIdMethods(); | ||
| 132 | $this->controller->clear_preview(); | ||
| 133 | $this->controller->determineId(); | ||
| 134 | |||
| 135 | // Now, if there is a backend user logged in and he has NO access to this page, | ||
| 136 |         // then re-evaluate the id shown! _GP('ADMCMD_noBeUser') is placed here because | ||
| 137 | // \TYPO3\CMS\Version\Hook\PreviewHook might need to know if a backend user is logged in. | ||
| 138 | if ( | ||
| 139 | $this->controller->isBackendUserLoggedIn() | ||
| 140 |             && (!$GLOBALS['BE_USER']->extPageReadAccess($this->controller->page) || GeneralUtility::_GP('ADMCMD_noBeUser')) | ||
|  | |||
| 141 |         ) { | ||
| 142 | // Remove user | ||
| 143 | unset($GLOBALS['BE_USER']); | ||
| 144 | $this->controller->beUserLogin = false; | ||
| 145 | // Re-evaluate the page-id. | ||
| 146 | $this->controller->checkAlternativeIdMethods(); | ||
| 147 | $this->controller->clear_preview(); | ||
| 148 | $this->controller->determineId(); | ||
| 149 | } | ||
| 150 | |||
| 151 | $this->controller->makeCacheHash(); | ||
| 152 | $this->timeTracker->pull(); | ||
| 153 | |||
| 154 | // Admin Panel & Frontend editing | ||
| 155 |         if ($this->controller->isBackendUserLoggedIn()) { | ||
| 156 | $GLOBALS['BE_USER']->initializeFrontendEdit(); | ||
| 157 |             if ($GLOBALS['BE_USER']->adminPanel instanceof AdminPanelView) { | ||
| 158 | $this->bootstrap->initializeLanguageObject(); | ||
| 159 | } | ||
| 160 |             if ($GLOBALS['BE_USER']->frontendEdit instanceof FrontendEditingController) { | ||
| 161 | $GLOBALS['BE_USER']->frontendEdit->initConfigOptions(); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | // Starts the template | ||
| 166 |         $this->timeTracker->push('Start Template', ''); | ||
| 167 | $this->controller->initTemplate(); | ||
| 168 | $this->timeTracker->pull(); | ||
| 169 | // Get from cache | ||
| 170 |         $this->timeTracker->push('Get Page from cache', ''); | ||
| 171 | $this->controller->getFromCache(); | ||
| 172 | $this->timeTracker->pull(); | ||
| 173 | // Get config if not already gotten | ||
| 174 | // After this, we should have a valid config-array ready | ||
| 175 | $this->controller->getConfigArray(); | ||
| 176 | // Setting language and locale | ||
| 177 |         $this->timeTracker->push('Setting language and locale', ''); | ||
| 178 | $this->controller->settingLanguage(); | ||
| 179 | $this->controller->settingLocale(); | ||
| 180 | $this->timeTracker->pull(); | ||
| 181 | |||
| 182 | // Convert POST data to utf-8 for internal processing if metaCharset is different | ||
| 183 | $this->controller->convPOSTCharset(); | ||
| 184 | |||
| 185 | $this->controller->initializeRedirectUrlHandlers(); | ||
| 186 | |||
| 187 | $this->controller->handleDataSubmission(); | ||
| 188 | |||
| 189 | // Check for shortcut page and redirect | ||
| 190 | $this->controller->checkPageForShortcutRedirect(); | ||
| 191 | $this->controller->checkPageForMountpointRedirect(); | ||
| 192 | |||
| 193 | // Generate page | ||
| 194 | $this->controller->setUrlIdToken(); | ||
| 195 |         $this->timeTracker->push('Page generation', ''); | ||
| 196 |         if ($this->controller->isGeneratePage()) { | ||
| 197 | $this->controller->generatePage_preProcessing(); | ||
| 198 | $this->controller->preparePageContentGeneration(); | ||
| 199 | // Content generation | ||
| 200 |             if (!$this->controller->isINTincScript()) { | ||
| 201 | PageGenerator::renderContent(); | ||
| 202 | $this->controller->setAbsRefPrefix(); | ||
| 203 | } | ||
| 204 | $this->controller->generatePage_postProcessing(); | ||
| 205 |         } elseif ($this->controller->isINTincScript()) { | ||
| 206 | $this->controller->preparePageContentGeneration(); | ||
| 207 | } | ||
| 208 | $this->controller->releaseLocks(); | ||
| 209 | $this->timeTracker->pull(); | ||
| 210 | |||
| 211 | // Render non-cached parts | ||
| 212 |         if ($this->controller->isINTincScript()) { | ||
| 213 |             $this->timeTracker->push('Non-cached objects', ''); | ||
| 214 | $this->controller->INTincScript(); | ||
| 215 | $this->timeTracker->pull(); | ||
| 216 | } | ||
| 217 | |||
| 218 | // Output content | ||
| 219 | $sendTSFEContent = false; | ||
| 220 |         if ($this->controller->isOutputting()) { | ||
| 221 |             $this->timeTracker->push('Print Content', ''); | ||
| 222 | $this->controller->processOutput(); | ||
| 223 | $sendTSFEContent = true; | ||
| 224 | $this->timeTracker->pull(); | ||
| 225 | } | ||
| 226 | // Store session data for fe_users | ||
| 227 | $this->controller->storeSessionData(); | ||
| 228 | |||
| 229 | // Create a Response object when sending content | ||
| 230 |         if ($sendTSFEContent) { | ||
| 231 | $response = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\Response::class); | ||
| 232 | } | ||
| 233 | |||
| 234 | // Statistics | ||
| 235 | $GLOBALS['TYPO3_MISC']['microtime_end'] = microtime(true); | ||
| 236 |         if ($sendTSFEContent) { | ||
| 237 | View Code Duplication |             if (isset($this->controller->config['config']['debug'])) { | |
| 238 | $includeParseTime = (bool)$this->controller->config['config']['debug']; | ||
| 239 |             } else { | ||
| 240 | $includeParseTime = !empty($GLOBALS['TYPO3_CONF_VARS']['FE']['debug']); | ||
| 241 | } | ||
| 242 |             if ($includeParseTime) { | ||
| 243 |                 $response = $response->withHeader('X-TYPO3-Parsetime', $this->timeTracker->getParseTime() . 'ms'); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | $this->controller->redirectToExternalUrl(); | ||
| 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 | |||
| 254 | // Admin panel | ||
| 255 |         if ($this->controller->isBackendUserLoggedIn() && $GLOBALS['BE_USER'] instanceof FrontendBackendUserAuthentication) { | ||
| 256 |             if ($GLOBALS['BE_USER']->isAdminPanelVisible()) { | ||
| 257 |                 $this->controller->content = str_ireplace('</body>', $GLOBALS['BE_USER']->displayAdminPanel() . '</body>', $this->controller->content); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 |         if ($sendTSFEContent) { | ||
| 262 | // Send content-length header. | ||
| 263 | // Notice that all HTML content outside the length of the content-length header will be cut off! | ||
| 264 | // Therefore content of unknown length from included PHP-scripts and if admin users are logged | ||
| 265 | // in (admin panel might show...) or if debug mode is turned on, we disable it! | ||
| 266 | if ( | ||
| 267 | (!isset($this->controller->config['config']['enableContentLengthHeader']) || $this->controller->config['config']['enableContentLengthHeader']) | ||
| 268 | && !$this->controller->beUserLogin && !$GLOBALS['TYPO3_CONF_VARS']['FE']['debug'] | ||
| 269 | && !$this->controller->config['config']['debug'] && !$this->controller->doWorkspacePreview() | ||
| 270 |             ) { | ||
| 271 |                 header('Content-Length: ' . strlen($this->controller->content)); | ||
| 272 | } | ||
| 273 | $response->getBody()->write($this->controller->content); | ||
| 274 | } | ||
| 275 | GeneralUtility::makeInstance(LogManager::class) | ||
| 276 |                       ->getLogger(get_class())->debug('END of FRONTEND session', ['_FLUSH' => true]); | ||
| 277 | return $response; | ||
| 278 | } | ||
| 279 | |||
| 280 | /** | ||
| 281 | * This request handler can handle any frontend request. | ||
| 282 | * | ||
| 283 | * @param \Psr\Http\Message\ServerRequestInterface $request | ||
| 284 | * @return bool If the request is not an eID request, TRUE otherwise FALSE | ||
| 285 | */ | ||
| 286 | public function canHandleRequest(\Psr\Http\Message\ServerRequestInterface $request) | ||
| 287 |     { | ||
| 288 | return $request->getQueryParams()['eID'] || $request->getParsedBody()['eID'] ? false : true; | ||
| 289 | } | ||
| 290 | |||
| 291 | /** | ||
| 292 | * Returns the priority - how eager the handler is to actually handle the | ||
| 293 | * request. | ||
| 294 | * | ||
| 295 | * @return int The priority of the request handler. | ||
| 296 | */ | ||
| 297 | public function getPriority() | ||
| 298 |     { | ||
| 299 | return 50; | ||
| 300 | } | ||
| 301 | |||
| 302 | /** | ||
| 303 | * Initializes output compression when enabled, could be split up and put into Bootstrap | ||
| 304 | * at a later point | ||
| 305 | */ | ||
| 306 | protected function initializeOutputCompression() | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | /** | ||
| 317 | * Timetracking started depending if a Backend User is logged in | ||
| 318 | */ | ||
| 319 | protected function initializeTimeTracker() | ||
| 320 |     { | ||
| 321 | $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']) ?: 'be_typo_user'; | ||
| 322 | |||
| 323 | /** @var TimeTracker timeTracker */ | ||
| 324 | $this->timeTracker = GeneralUtility::makeInstance(TimeTracker::class, ($this->request->getCookieParams()[$configuredCookieName] ? true : false)); | ||
| 325 | $this->timeTracker->start(); | ||
| 326 | } | ||
| 327 | |||
| 328 | /** | ||
| 329 | * Creates an instance of TSFE and sets it as a global variable | ||
| 330 | */ | ||
| 331 | protected function initializeController() | ||
| 349 | } | ||
| 350 | } | ||
| 351 | 
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: