Breaking: #102621 - Most TSFE members marked internal or read-only

See forge#102621

Description

Most properties and methods of class TypoScriptFrontendController have been marked @internal or "read-only".

TypoScriptFrontendController ("TSFE") is a god object within the TYPO3 frontend rendering chain: It is used by multiple middlewares that call TSFE methods to create state in it, it is used within ContentObjectRenderer and various other classes to update and retrieve state. It is also registered as $GLOBALS['TSFE'] at some point and thus available as global state object.

This makes the class the biggest anti-pattern we have within the frontend - class ContentObjectRenderer is problematic as well, but that is a different story. The current role of TypoScriptFrontendController leads to very complex and opaque state handling within the frontend rendering, is the true source of many hard to fix issues and prevents Core development from implementing cool new features.

The TYPO3 Core strives to resolve large parts of this with TYPO3 v13: State needed by lower level code is being modeled as request attributes or handled locally in middlewares, methods are moved out of the class into middlewares to improve encapsulation and code flow.

To do that within continued TYPO3 v13 development, the Core needs to mark various methods and properties @internal, and needs to mark more strict access patterns on others.

The solution is to look at public properties of TypoScriptFrontendController, and to declare those as @internal, which extensions typically should not need to deal with at all. Others (like for instance id) are actively used by extensions and will be substituted by something different later, and are thus marked as "allowed to read, but never write" for extensions. This allows implementation of a deprecation layer for those "read-only" properties later, while those marked @internal can vanish without further notice. A similar strategy is added for methods, leaving only a few not marked @internal, which the Core will deprecate with a compatibility layer later.

The following public class properties have been marked "read-only":

  • TypoScriptFrontendController->id - Use $request->getAttribute('frontend.page.information')->getId() instead

  • TypoScriptFrontendController->rootLine - Use $request->getAttribute('frontend.page.information')->getRootLine() instead

  • TypoScriptFrontendController->page - Use $request->getAttribute('frontend.page.information')->getPageRecord() instead

  • TypoScriptFrontendController->contentPid - Avoid usages altogether, available as @internal call using $request->getAttribute('frontend.page.information')->getContentFromPid()

  • TypoScriptFrontendController->sys_page - Avoid altogether, create own instance using GeneralUtility::makeInstance(PageRepository::class)

  • TypoScriptFrontendController->config - Reading $tsfe->config['config'] and $tsfe->config['rootLine'] is allowed

  • TypoScriptFrontendController->absRefPrefix

  • TypoScriptFrontendController->cObj

  • TypoScriptFrontendController->config['rootLine'] - Use $request->getAttribute('frontend.page.information')->getLocalRootLine() instead

The following public class properties have been marked @internal - in general all properties not listed above:

  • TypoScriptFrontendController->no_cache - Use request attribute frontend.cache.instruction instead

  • TypoScriptFrontendController->additionalHeaderData

  • TypoScriptFrontendController->additionalFooterData

  • TypoScriptFrontendController->register

  • TypoScriptFrontendController->registerStack

  • TypoScriptFrontendController->recordRegister

  • TypoScriptFrontendController->currentRecord

  • TypoScriptFrontendController->content

  • TypoScriptFrontendController->lastImgResourceInfo

The following methods have been marked @internal:

  • TypoScriptFrontendController->__construct() - extensions should not create own instances of TSFE

  • TypoScriptFrontendController->determineId()

  • TypoScriptFrontendController->getPageAccessFailureReasons()

  • TypoScriptFrontendController->calculateLinkVars()

  • TypoScriptFrontendController->isGeneratePage()

  • TypoScriptFrontendController->preparePageContentGeneration()

  • TypoScriptFrontendController->generatePage_postProcessing()

  • TypoScriptFrontendController->generatePageTitle()

  • TypoScriptFrontendController->INTincScript()

  • TypoScriptFrontendController->INTincScript_loadJSCode()

  • TypoScriptFrontendController->isINTincScript()

  • TypoScriptFrontendController->applyHttpHeadersToResponse()

  • TypoScriptFrontendController->isStaticCacheble()

  • TypoScriptFrontendController->newCObj()

  • TypoScriptFrontendController->logDeprecatedTyposcript()

  • TypoScriptFrontendController->uniqueHash()

  • TypoScriptFrontendController->set_cache_timeout_default()

  • TypoScriptFrontendController->get_cache_timeout()

  • TypoScriptFrontendController->getRequestedId() - Use $request->getAttribute('routing')->getPageId() instead

  • TypoScriptFrontendController->getLanguage()

  • TypoScriptFrontendController->getSite()

  • TypoScriptFrontendController->getContext() - Use dependency injection or GeneralUtility::makeInstance() instead

  • TypoScriptFrontendController->getPageArguments()

Impact

Writing to the listed read-only properties may break the frontend rendering, using the properties or methods marked as @internal may raise fatal PHP errors.

Affected installations

The majority of extensions should already use the above properties that are marked read-only for reading only: Updating their state can easily lead to unexpected behavior. Most extensions also don't consume the properties or methods marked as @internal.

Extension developers should watch out for usages of TypoScriptFrontendController in general and reduce usages as much as possible.

Migration

The migration strategy depends on the specific use case. The frontend rendering chain continues to add state that is needed by extensions as PSR-7 request attributes. Debugging the incoming request within an extension often reveals a proper alternative.