[BUGFIX] Include Records with "All Languages" in default language
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / ContentObjectRenderer.php
index fa4d39c..677625b 100644 (file)
@@ -24,15 +24,16 @@ use TYPO3\CMS\Core\Context\LanguageAspect;
 use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 use TYPO3\CMS\Core\Database\Query\QueryHelper;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
-use TYPO3\CMS\Core\FrontendEditing\FrontendEditingController;
 use TYPO3\CMS\Core\Html\HtmlParser;
 use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
 use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
 use TYPO3\CMS\Core\LinkHandling\LinkService;
+use TYPO3\CMS\Core\Log\LogManager;
 use TYPO3\CMS\Core\Mail\MailMessage;
 use TYPO3\CMS\Core\Resource\Exception;
 use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
@@ -42,19 +43,21 @@ use TYPO3\CMS\Core\Resource\FileReference;
 use TYPO3\CMS\Core\Resource\Folder;
 use TYPO3\CMS\Core\Resource\ProcessedFile;
 use TYPO3\CMS\Core\Resource\ResourceFactory;
-use TYPO3\CMS\Core\Resource\StorageRepository;
 use TYPO3\CMS\Core\Service\DependencyOrderingService;
 use TYPO3\CMS\Core\Service\FlexFormService;
 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
+use TYPO3\CMS\Core\Site\Entity\Site;
+use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
 use TYPO3\CMS\Core\TypoScript\TypoScriptService;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\DebugUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\HttpUtility;
 use TYPO3\CMS\Core\Utility\MailUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
-use TYPO3\CMS\Core\Utility\PathUtility;
 use TYPO3\CMS\Core\Utility\StringUtility;
 use TYPO3\CMS\Core\Versioning\VersionState;
 use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
@@ -117,8 +120,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
         'cObject' => 'cObject',
         'cObject.' => 'array',
         'numRows.' => 'array',
-        'filelist' => 'dir',
-        'filelist.' => 'array',
         'preUserFunc' => 'functionName',
         'stdWrapOverride' => 'hook',
         // this is a placeholder for the second Hook
@@ -206,8 +207,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
         'innerWrap.' => 'array',
         'innerWrap2' => 'wrap',
         'innerWrap2.' => 'array',
-        'addParams.' => 'array',
-        'filelink.' => 'array',
         'preCObject' => 'cObject',
         'preCObject.' => 'array',
         'postCObject' => 'cObject',
@@ -348,9 +347,9 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public $checkPid_cache = [];
 
     /**
-     * @var string
+     * @var string|int
      */
-    public $checkPid_badDoktypeList = '255';
+    public $checkPid_badDoktypeList = PageRepository::DOKTYPE_RECYCLER;
 
     /**
      * This will be set by typoLink() to the url of the most recent link created.
@@ -437,11 +436,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
     protected $typoScriptFrontendController;
 
     /**
-     * @var MarkerBasedTemplateService
-     */
-    protected $templateService;
-
-    /**
      * Indicates that object type is USER.
      *
      * @see ContentObjectRender::$userObjectType
@@ -461,7 +455,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
     {
         $this->typoScriptFrontendController = $typoScriptFrontendController;
         $this->contentObjectClassMap = $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'];
-        $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
     }
 
     /**
@@ -474,7 +467,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function __sleep()
     {
         $vars = get_object_vars($this);
-        unset($vars['typoScriptFrontendController']);
+        unset($vars['typoScriptFrontendController'], $vars['logger']);
         if ($this->currentFile instanceof FileReference) {
             $this->currentFile = 'FileReference:' . $this->currentFile->getUid();
         } elseif ($this->currentFile instanceof File) {
@@ -507,6 +500,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                 $this->currentFile = null;
             }
         }
+        $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
     }
 
     /**
@@ -611,7 +605,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      *
      * @param array $data The record array
      * @param string $currentRecord This is set to the [table]:[uid] of the record delivered in the $data-array, if the cObjects CONTENT or RECORD is in operation. Note that $GLOBALS['TSFE']->currentRecord is set to an equal value but always indicating the latest record rendered.
-     * @access private
+     * @internal
      */
     public function setParent($data, $currentRecord)
     {
@@ -697,7 +691,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                 $timeTracker->push($TSkey, $name);
             }
             // Checking if the COBJ is a reference to another object. (eg. name of 'blabla.blabla = < styles.something')
-            if ($name[0] === '<') {
+            if (isset($name[0]) && $name[0] === '<') {
                 $key = trim(substr($name, 1));
                 $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
                 // $name and $conf is loaded with the referenced values.
@@ -733,7 +727,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                                 if (!$hookObject instanceof ContentObjectGetSingleHookInterface) {
                                     throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetSingleHookInterface::class, 1195043731);
                                 }
-                                /** @var $hookObject ContentObjectGetSingleHookInterface */
+                                /** @var ContentObjectGetSingleHookInterface $hookObject */
                                 $content .= $hookObject->getSingleContentObject($name, (array)$conf, $TSkey, $this);
                             }
                         } else {
@@ -825,7 +819,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
         if ($cacheConfiguration !== null) {
             $key = $this->calculateCacheKey($cacheConfiguration);
             if (!empty($key)) {
-                /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
+                /** @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cacheFrontend */
                 $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
                 $tags = $this->calculateCacheTags($cacheConfiguration);
                 $lifetime = $this->calculateCacheLifetime($cacheConfiguration);
@@ -999,7 +993,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $pidList A list of page Content-Element PIDs (Page UIDs) / stdWrap
      * @param array $pidConf stdWrap array for the list
      * @return string A list of PIDs
-     * @access private
+     * @internal
      */
     public function getSlidePids($pidList, $pidConf)
     {
@@ -1033,7 +1027,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $file File TypoScript resource
      * @param array $conf TypoScript configuration properties
      * @return string <img> tag, (possibly wrapped in links and other HTML) if any image found.
-     * @access private
+     * @internal
      * @see IMAGE()
      */
     public function cImage($file, $conf)
@@ -1073,7 +1067,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
             'selfClosingTagSlash' => !empty($tsfe->xhtmlDoctype) ? ' /' : '',
         ];
 
-        $theValue = $this->templateService->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
+        $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
+        $theValue = $markerTemplateEngine->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
 
         $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
         if ($linkWrap) {
@@ -1121,7 +1116,10 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function getImageTagTemplate($layoutKey, $conf)
     {
         if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
-            $imageTagLayout = $this->stdWrap($conf['layout.'][$layoutKey . '.']['element'], $conf['layout.'][$layoutKey . '.']['element.']);
+            $imageTagLayout = $this->stdWrap(
+                $conf['layout.'][$layoutKey . '.']['element'] ?? '',
+                $conf['layout.'][$layoutKey . '.']['element.'] ?? []
+            );
         } else {
             $imageTagLayout = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
         }
@@ -1140,7 +1138,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function getImageSourceCollection($layoutKey, $conf, $file)
     {
         $sourceCollection = '';
-        if ($layoutKey && $conf['sourceCollection.'] && ($conf['layout.'][$layoutKey . '.']['source'] || $conf['layout.'][$layoutKey . '.']['source.'])) {
+        if ($layoutKey
+            && isset($conf['sourceCollection.']) && $conf['sourceCollection.']
+            && (
+                isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source']
+                || isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.']
+            )
+        ) {
 
             // find active sourceCollection
             $activeSourceCollections = [];
@@ -1159,11 +1163,14 @@ class ContentObjectRenderer implements LoggerAwareInterface
 
             // render sources
             foreach ($activeSourceCollections as $key => $sourceConfiguration) {
-                $sourceLayout = $this->stdWrap($srcLayoutOptionSplitted[$key]['source'], $srcLayoutOptionSplitted[$key]['source.']);
+                $sourceLayout = $this->stdWrap(
+                    $srcLayoutOptionSplitted[$key]['source'] ?? '',
+                    $srcLayoutOptionSplitted[$key]['source.'] ?? []
+                );
 
                 $sourceRenderConfiguration = [
                     'file' => $file,
-                    'file.' => $conf['file.']
+                    'file.' => $conf['file.'] ?? null
                 ];
 
                 if (isset($sourceConfiguration['quality']) || isset($sourceConfiguration['quality.'])) {
@@ -1177,15 +1184,24 @@ class ContentObjectRenderer implements LoggerAwareInterface
                 }
 
                 if (isset($sourceConfiguration['pixelDensity'])) {
-                    $pixelDensity = (int)$this->stdWrap($sourceConfiguration['pixelDensity'], $sourceConfiguration['pixelDensity.']);
+                    $pixelDensity = (int)$this->stdWrap(
+                        $sourceConfiguration['pixelDensity'] ?? '',
+                        $sourceConfiguration['pixelDensity.'] ?? []
+                    );
                 } else {
                     $pixelDensity = 1;
                 }
                 $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
                 foreach ($dimensionKeys as $dimensionKey) {
-                    $dimension = $this->stdWrap($sourceConfiguration[$dimensionKey], $sourceConfiguration[$dimensionKey . '.']);
+                    $dimension = $this->stdWrap(
+                        $sourceConfiguration[$dimensionKey] ?? '',
+                        $sourceConfiguration[$dimensionKey . '.'] ?? []
+                    );
                     if (!$dimension) {
-                        $dimension = $this->stdWrap($conf['file.'][$dimensionKey], $conf['file.'][$dimensionKey . '.']);
+                        $dimension = $this->stdWrap(
+                            $conf['file.'][$dimensionKey] ?? '',
+                            $conf['file.'][$dimensionKey . '.'] ?? []
+                        );
                     }
                     if ($dimension) {
                         if (strstr($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
@@ -1219,7 +1235,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
                     $sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : '';
 
-                    $oneSourceCollection = $this->templateService->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
+                    $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
+                    $oneSourceCollection = $markerTemplateEngine->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
 
                     foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] ?? [] as $className) {
                         $hookObject = GeneralUtility::makeInstance($className);
@@ -1387,11 +1404,12 @@ class ContentObjectRenderer implements LoggerAwareInterface
     {
         $wrapArr = explode('|', $wrap);
         if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
-            if ($uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid']) {
+            $uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid'] ?? null;
+            if ($uid) {
                 $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
             }
         }
-        return trim($wrapArr[0]) . $content . trim($wrapArr[1]);
+        return trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
     }
 
     /**
@@ -1407,7 +1425,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     {
         $altText = isset($conf['altText.']) ? trim($this->stdWrap($conf['altText'], $conf['altText.'])) : trim($conf['altText']);
         $titleText = isset($conf['titleText.']) ? trim($this->stdWrap($conf['titleText'], $conf['titleText.'])) : trim($conf['titleText']);
-        if (isset($conf['longdescURL.']) && $this->getTypoScriptFrontendController()->config['config']['doctype'] != 'html5') {
+        if (isset($conf['longdescURL.']) && $this->getTypoScriptFrontendController()->config['config']['doctype'] !== 'html5') {
             $longDescUrl = $this->typoLink_URL($conf['longdescURL.']);
         } else {
             $longDescUrl = trim($conf['longdescURL']);
@@ -1443,9 +1461,9 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function getATagParams($conf, $addGlobal = 1)
     {
         $aTagParams = '';
-        if ($conf['ATagParams.']) {
+        if ($conf['ATagParams.'] ?? false) {
             $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
-        } elseif ($conf['ATagParams']) {
+        } elseif ($conf['ATagParams'] ?? false) {
             $aTagParams = ' ' . $conf['ATagParams'];
         }
         if ($addGlobal) {
@@ -1480,7 +1498,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function extLinkATagParams($URL, $TYPE)
     {
         $out = '';
-        if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']) {
+        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler'])) {
             $extLinkATagParamsHandler = GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']);
             if (method_exists($extLinkATagParamsHandler, 'main')) {
                 $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
@@ -1571,20 +1589,22 @@ class ContentObjectRenderer implements LoggerAwareInterface
             if ((!isset($isExecuted[$stdWrapName]) || !$isExecuted[$stdWrapName]) && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
                 $functionName = rtrim($stdWrapName, '.');
                 $functionProperties = $functionName . '.';
-                $functionType = $this->stdWrapOrder[$functionName];
+                $functionType = $this->stdWrapOrder[$functionName] ?? null;
                 // If there is any code on the next level, check if it contains "official" stdWrap functions
                 // if yes, execute them first - will make each function stdWrap aware
                 // so additional stdWrap calls within the functions can be removed, since the result will be the same
                 if (!empty($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
                     if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
-                        $conf[$functionName] = $this->stdWrap($conf[$functionName], $conf[$functionProperties]);
+                        $conf[$functionName] = $this->stdWrap($conf[$functionName] ?? '', $conf[$functionProperties] ?? []);
                     }
                 }
                 // Check if key is still containing something, since it might have been changed by next level stdWrap before
-                if ((isset($conf[$functionName]) || $conf[$functionProperties]) && ($functionType !== 'boolean' || $conf[$functionName])) {
+                if ((isset($conf[$functionName]) || $conf[$functionProperties])
+                    && ($functionType !== 'boolean' || $conf[$functionName])
+                ) {
                     // Get just that part of $conf that is needed for the particular function
                     $singleConf = [
-                        $functionName => $conf[$functionName],
+                        $functionName => $conf[$functionName] ?? null,
                         $functionProperties => $conf[$functionProperties] ?? null
                     ];
                     // Hand over the whole $conf array to the stdWrapHookObjects
@@ -1708,7 +1728,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_setCurrent($content = '', $conf = [])
     {
-        $this->data[$this->currentValKey] = $conf['setCurrent'];
+        $this->data[$this->currentValKey] = $conf['setCurrent'] ?? null;
         return $content;
     }
 
@@ -1722,9 +1742,16 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_lang($content = '', $conf = [])
     {
-        $tsfe = $this->getTypoScriptFrontendController();
-        if (isset($conf['lang.']) && $tsfe->config['config']['language'] && isset($conf['lang.'][$tsfe->config['config']['language']])) {
-            $content = $conf['lang.'][$tsfe->config['config']['language']];
+        $request = $GLOBALS['TYPO3_REQUEST'] ?? null;
+        $siteLanguage = $request ? $request->getAttribute('language') : null;
+        if ($siteLanguage instanceof SiteLanguage) {
+            $currentLanguageCode = $siteLanguage->getTypo3Language();
+        } else {
+            $tsfe = $this->getTypoScriptFrontendController();
+            $currentLanguageCode = $tsfe->config['config']['language'] ?? null;
+        }
+        if ($currentLanguageCode && isset($conf['lang.'][$currentLanguageCode])) {
+            $content = $conf['lang.'][$currentLanguageCode];
         }
         return $content;
     }
@@ -1783,7 +1810,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_cObject($content = '', $conf = [])
     {
-        return $this->cObjGetSingle($conf['cObject'], $conf['cObject.'], '/stdWrap/.cObject');
+        return $this->cObjGetSingle($conf['cObject'] ?? '', $conf['cObject.'] ?? [], '/stdWrap/.cObject');
     }
 
     /**
@@ -1801,19 +1828,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
     }
 
     /**
-     * filelist
-     * Will create a list of files based on some additional parameters
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for filelist.
-     * @return string The processed input value
-     */
-    public function stdWrap_filelist($content = '', $conf = [])
-    {
-        return $this->filelist($conf['filelist']);
-    }
-
-    /**
      * preUserFunc
      * Will execute a user public function before the content will be modified by any other stdWrap function
      *
@@ -1854,7 +1868,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_override($content = '', $conf = [])
     {
-        if (trim($conf['override'])) {
+        if (trim($conf['override'] ?? false)) {
             $content = $conf['override'];
         }
         return $content;
@@ -1871,7 +1885,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_preIfEmptyListNum($content = '', $conf = [])
     {
-        return $this->listNum($content, $conf['preIfEmptyListNum'], $conf['preIfEmptyListNum.']['splitChar']);
+        return $this->listNum($content, $conf['preIfEmptyListNum'] ?? null, $conf['preIfEmptyListNum.']['splitChar'] ?? null);
     }
 
     /**
@@ -1933,7 +1947,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_listNum($content = '', $conf = [])
     {
-        return $this->listNum($content, $conf['listNum'], $conf['listNum.']['splitChar']);
+        return $this->listNum($content, $conf['listNum'] ?? null, $conf['listNum.']['splitChar'] ?? null);
     }
 
     /**
@@ -2062,7 +2076,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_fieldRequired($content = '', $conf = [])
     {
-        if (!trim($this->data[$conf['fieldRequired']])) {
+        if (!trim($this->data[$conf['fieldRequired'] ?? null] ?? '')) {
             $content = '';
             $this->stopRendering[$this->stdWrapRecursionLevel] = true;
         }
@@ -2113,7 +2127,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_HTMLparser($content = '', $conf = [])
     {
-        if (is_array($conf['HTMLparser.'])) {
+        if (isset($conf['HTMLparser.']) && is_array($conf['HTMLparser.'])) {
             $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
         }
         return $content;
@@ -2158,7 +2172,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function stdWrap_prioriCalc($content = '', $conf = [])
     {
         $content = MathUtility::calculateWithParentheses($content);
-        if ($conf['prioriCalc'] === 'intval') {
+        if (!empty($conf['prioriCalc']) && $conf['prioriCalc'] === 'intval') {
             $content = (int)$content;
         }
         return $content;
@@ -2233,7 +2247,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_numberFormat($content = '', $conf = [])
     {
-        return $this->numberFormat($content, $conf['numberFormat.']);
+        return $this->numberFormat($content, $conf['numberFormat.'] ?? []);
     }
 
     /**
@@ -2261,7 +2275,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     {
         // Check for zero length string to mimic default case of date/gmdate.
         $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
-        $content = $conf['date.']['GMT'] ? gmdate($conf['date'], $content) : date($conf['date'], $content);
+        $content = !empty($conf['date.']['GMT']) ? gmdate($conf['date'] ?? null, $content) : date($conf['date'] ?? null, $content);
         return $content;
     }
 
@@ -2278,7 +2292,9 @@ class ContentObjectRenderer implements LoggerAwareInterface
     {
         // Check for zero length string to mimic default case of strtime/gmstrftime
         $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
-        $content = $conf['strftime.']['GMT'] ? gmstrftime($conf['strftime'], $content) : strftime($conf['strftime'], $content);
+        $content = (isset($conf['strftime.']['GMT']) && $conf['strftime.']['GMT'])
+            ? gmstrftime($conf['strftime'] ?? null, $content)
+            : strftime($conf['strftime'] ?? null, $content);
         if (!empty($conf['strftime.']['charset'])) {
             $output = mb_convert_encoding($content, 'utf-8', trim(strtolower($conf['strftime.']['charset'])));
             return $output ?: $content;
@@ -2312,7 +2328,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_age($content = '', $conf = [])
     {
-        return $this->calcAge((int)$GLOBALS['EXEC_TIME'] - (int)$content, $conf['age']);
+        return $this->calcAge((int)($GLOBALS['EXEC_TIME'] ?? 0) - (int)$content, $conf['age'] ?? null);
     }
 
     /**
@@ -2365,7 +2381,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_cropHTML($content = '', $conf = [])
     {
-        return $this->cropHTML($content, $conf['cropHTML']);
+        return $this->cropHTML($content, $conf['cropHTML'] ?? '');
     }
 
     /**
@@ -2447,7 +2463,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_doubleBrTag($content = '', $conf = [])
     {
-        return preg_replace('/\R{1,2}[\t\x20]*\R{1,2}/', $conf['doubleBrTag'], $content);
+        return preg_replace('/\R{1,2}[\t\x20]*\R{1,2}/', $conf['doubleBrTag'] ?? null, $content);
     }
 
     /**
@@ -2473,7 +2489,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_brTag($content = '', $conf = [])
     {
-        return str_replace(LF, $conf['brTag'], $content);
+        return str_replace(LF, $conf['brTag'] ?? null, $content);
     }
 
     /**
@@ -2513,7 +2529,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_innerWrap($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['innerWrap']);
+        return $this->wrap($content, $conf['innerWrap'] ?? null);
     }
 
     /**
@@ -2527,34 +2543,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_innerWrap2($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['innerWrap2']);
-    }
-
-    /**
-     * addParams
-     * Adds tag attributes to any content that is a tag
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for addParams.
-     * @return string The processed input value
-     */
-    public function stdWrap_addParams($content = '', $conf = [])
-    {
-        return $this->addParams($content, $conf['addParams.']);
-    }
-
-    /**
-     * filelink
-     * Used to make lists of links to files
-     * See wrap
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for filelink.
-     * @return string The processed input value
-     */
-    public function stdWrap_filelink($content = '', $conf = [])
-    {
-        return $this->filelink($content, $conf['filelink.']);
+        return $this->wrap($content, $conf['innerWrap2'] ?? null);
     }
 
     /**
@@ -2594,7 +2583,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_wrapAlign($content = '', $conf = [])
     {
-        $wrapAlign = trim($conf['wrapAlign']);
+        $wrapAlign = trim($conf['wrapAlign'] ?? '');
         if ($wrapAlign) {
             $content = $this->wrap($content, '<div style="text-align:' . htmlspecialchars($wrapAlign) . ';">|</div>');
         }
@@ -2630,7 +2619,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_wrap($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['wrap'], $conf['wrap.']['splitChar'] ? $conf['wrap.']['splitChar'] : '|');
+        return $this->wrap(
+            $content,
+            $conf['wrap'] ?? null,
+            $conf['wrap.']['splitChar'] ?? '|'
+        );
     }
 
     /**
@@ -2645,8 +2638,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function stdWrap_noTrimWrap($content = '', $conf = [])
     {
         $splitChar = isset($conf['noTrimWrap.']['splitChar.'])
-            ? $this->stdWrap($conf['noTrimWrap.']['splitChar'], $conf['noTrimWrap.']['splitChar.'])
-            : $conf['noTrimWrap.']['splitChar'];
+            ? $this->stdWrap($conf['noTrimWrap.']['splitChar'] ?? '', $conf['noTrimWrap.']['splitChar.'])
+            : $conf['noTrimWrap.']['splitChar'] ?? '';
         if ($splitChar === null || $splitChar === '') {
             $splitChar = '|';
         }
@@ -2669,7 +2662,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_wrap2($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['wrap2'], $conf['wrap2.']['splitChar'] ? $conf['wrap2.']['splitChar'] : '|');
+        return $this->wrap(
+            $content,
+            $conf['wrap2'] ?? null,
+            $conf['wrap2.']['splitChar'] ?? '|'
+        );
     }
 
     /**
@@ -2723,7 +2720,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_wrap3($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['wrap3'], $conf['wrap3.']['splitChar'] ? $conf['wrap3.']['splitChar'] : '|');
+        return $this->wrap(
+            $content,
+            $conf['wrap3'] ?? null,
+            $conf['wrap3.']['splitChar'] ?? '|'
+        );
     }
 
     /**
@@ -2738,7 +2739,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     {
         $sortedKeysArray = ArrayUtility::filterAndSortByNumericKeys($conf['orderedStdWrap.'], true);
         foreach ($sortedKeysArray as $key) {
-            $content = $this->stdWrap($content, $conf['orderedStdWrap.'][$key . '.']);
+            $content = $this->stdWrap($content, $conf['orderedStdWrap.'][$key . '.'] ?? null);
         }
         return $content;
     }
@@ -2753,7 +2754,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_outerWrap($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['outerWrap']);
+        return $this->wrap($content, $conf['outerWrap'] ?? null);
     }
 
     /**
@@ -2814,7 +2815,10 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function stdWrap_prefixComment($content = '', $conf = [])
     {
-        if (!$this->getTypoScriptFrontendController()->config['config']['disablePrefixComment'] && !empty($conf['prefixComment'])) {
+        if (
+            (!isset($this->getTypoScriptFrontendController()->config['config']['disablePrefixComment']) || !$this->getTypoScriptFrontendController()->config['config']['disablePrefixComment'])
+            && !empty($conf['prefixComment'])
+        ) {
             $content = $this->prefixComment($conf['prefixComment'], [], $content);
         }
         return $content;
@@ -2831,7 +2835,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     public function stdWrap_editIcons($content = '', $conf = [])
     {
         if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $conf['editIcons']) {
-            if (!is_array($conf['editIcons.'])) {
+            if (!isset($conf['editIcons.']) || !is_array($conf['editIcons.'])) {
                 $conf['editIcons.'] = [];
             }
             $content = $this->editIcons($content, $conf['editIcons'], $conf['editIcons.']);
@@ -2871,7 +2875,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
         if (empty($key)) {
             return $content;
         }
-        /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
+        /** @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cacheFrontend */
         $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
         $tags = $this->calculateCacheTags($conf['cache.']);
         $lifetime = $this->calculateCacheLifetime($conf['cache.']);
@@ -2954,7 +2958,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      *
      * @param array $conf TypoScript properties for the property (see link to "numRows")
      * @return int The number of rows found by the select
-     * @access private
+     * @internal
      * @see stdWrap()
      */
     public function numRows($conf)
@@ -3031,7 +3035,9 @@ class ContentObjectRenderer implements LoggerAwareInterface
             }
         }
         if ($flag) {
-            $value = isset($conf['value.']) ? trim($this->stdWrap($conf['value'], $conf['value.'])) : trim($conf['value']);
+            $value = isset($conf['value.'])
+                ? trim($this->stdWrap($conf['value'] ?? '', $conf['value.']))
+                : trim($conf['value'] ?? '');
             if (isset($conf['isGreaterThan']) || isset($conf['isGreaterThan.'])) {
                 $number = isset($conf['isGreaterThan.']) ? trim($this->stdWrap($conf['isGreaterThan'], $conf['isGreaterThan.'])) : trim($conf['isGreaterThan']);
                 if ($number <= $value) {
@@ -3057,117 +3063,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
                 }
             }
         }
-        if ($conf['negate']) {
+        if ($conf['negate'] ?? false) {
             $flag = !$flag;
         }
         return $flag;
     }
 
     /**
-     * Reads a directory for files and returns the filepaths in a string list separated by comma.
-     * Implements the stdWrap property "filelist"
-     *
-     * @param string $data The command which contains information about what files/directory listing to return. See the "filelist" property of stdWrap for details.
-     * @return string Comma list of files.
-     * @access private
-     * @see stdWrap()
-     */
-    public function filelist($data)
-    {
-        $data = trim($data);
-        if ($data === '') {
-            return '';
-        }
-        list($possiblePath, $ext_list, $sorting, $reverse, $useFullPath) = GeneralUtility::trimExplode('|', $data);
-        // read directory:
-        // MUST exist!
-        $path = '';
-        // proceeds if no '//', '..' or '\' is in the $theFile
-        if (GeneralUtility::validPathStr($possiblePath)) {
-            // Removes all dots, slashes and spaces after a path.
-            $possiblePath = preg_replace('/[\\/\\. ]*$/', '', $possiblePath);
-            if (!GeneralUtility::isAbsPath($possiblePath) && @is_dir($possiblePath)) {
-                // Now check if it matches one of the FAL storages
-                $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
-                $storages = $storageRepository->findAll();
-                foreach ($storages as $storage) {
-                    if ($storage->getDriverType() === 'Local' && $storage->isPublic() && $storage->isOnline()) {
-                        $folder = $storage->getPublicUrl($storage->getRootLevelFolder(), true);
-                        if (GeneralUtility::isFirstPartOfStr($possiblePath . '/', $folder)) {
-                            $path = $possiblePath;
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-        if (!$path) {
-            return '';
-        }
-        $items = [
-            'files' => [],
-            'sorting' => []
-        ];
-        $ext_list = strtolower(GeneralUtility::uniqueList($ext_list));
-        // Read dir:
-        $d = @dir($path);
-        if (is_object($d)) {
-            $count = 0;
-            while ($entry = $d->read()) {
-                if ($entry !== '.' && $entry !== '..') {
-                    // Because of odd PHP-error where <br />-tag is sometimes placed after a filename!!
-                    $wholePath = $path . '/' . $entry;
-                    if (file_exists($wholePath) && filetype($wholePath) === 'file') {
-                        $info = GeneralUtility::split_fileref($wholePath);
-                        if (!$ext_list || GeneralUtility::inList($ext_list, $info['fileext'])) {
-                            $items['files'][] = $info['file'];
-                            switch ($sorting) {
-                                case 'name':
-                                    $items['sorting'][] = strtolower($info['file']);
-                                    break;
-                                case 'size':
-                                    $items['sorting'][] = filesize($wholePath);
-                                    break;
-                                case 'ext':
-                                    $items['sorting'][] = $info['fileext'];
-                                    break;
-                                case 'date':
-                                    $items['sorting'][] = filectime($wholePath);
-                                    break;
-                                case 'mdate':
-                                    $items['sorting'][] = filemtime($wholePath);
-                                    break;
-                                default:
-                                    $items['sorting'][] = $count;
-                            }
-                            $count++;
-                        }
-                    }
-                }
-            }
-            $d->close();
-        }
-        // Sort if required
-        if (!empty($items['sorting'])) {
-            if (strtolower($reverse) !== 'r') {
-                asort($items['sorting']);
-            } else {
-                arsort($items['sorting']);
-            }
-        }
-        if (!empty($items['files'])) {
-            // Make list
-            reset($items['sorting']);
-            $list_arr = [];
-            foreach ($items['sorting'] as $key => $v) {
-                $list_arr[] = $useFullPath ? $path . '/' . $items['files'][$key] : $items['files'][$key];
-            }
-            return implode(',', $list_arr);
-        }
-        return '';
-    }
-
-    /**
      * Passes the input value, $theValue, to an instance of "\TYPO3\CMS\Core\Html\HtmlParser"
      * together with the TypoScript options which are first converted from a TS style array
      * to a set of arrays with options for the \TYPO3\CMS\Core\Html\HtmlParser class.
@@ -3269,7 +3171,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $content The string to perform the operation on
      * @param string $options The parameters to substring, given as a comma list of integers where the first and second number is passed as arg 1 and 2 to substr().
      * @return string The processed input value.
-     * @access private
+     * @internal
      * @see stdWrap()
      */
     public function substring($content, $options)
@@ -3287,15 +3189,15 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $content The string to perform the operation on
      * @param string $options The parameters splitted by "|": First parameter is the max number of chars of the string. Negative value means cropping from end of string. Second parameter is the pre/postfix string to apply if cropping occurs. Third parameter is a boolean value. If set then crop will be applied at nearest space.
      * @return string The processed input value.
-     * @access private
+     * @internal
      * @see stdWrap()
      */
     public function crop($content, $options)
     {
         $options = explode('|', $options);
         $chars = (int)$options[0];
-        $afterstring = trim($options[1]);
-        $crop2space = trim($options[2]);
+        $afterstring = trim($options[1] ?? '');
+        $crop2space = trim($options[2] ?? '');
         if ($chars) {
             if (mb_strlen($content, 'utf-8') > abs($chars)) {
                 $truncatePosition = false;
@@ -3327,7 +3229,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $content The string to perform the operation on
      * @param string $options The parameters splitted by "|": First parameter is the max number of chars of the string. Negative value means cropping from end of string. Second parameter is the pre/postfix string to apply if cropping occurs. Third parameter is a boolean value. If set then crop will be applied at nearest space.
      * @return string The processed input value.
-     * @access private
+     * @internal
      * @see stdWrap()
      */
     public function cropHTML($content, $options)
@@ -3335,8 +3237,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
         $options = explode('|', $options);
         $chars = (int)$options[0];
         $absChars = abs($chars);
-        $replacementForEllipsis = trim($options[1]);
-        $crop2space = trim($options[2]) === '1';
+        $replacementForEllipsis = trim($options[1] ?? '');
+        $crop2space = trim($options[2] ?? '') === '1';
         // Split $content into an array(even items in the array are outside the tags, odd numbers are tag-blocks).
         $tags = 'a|abbr|address|area|article|aside|audio|b|bdi|bdo|blockquote|body|br|button|caption|cite|code|col|colgroup|data|datalist|dd|del|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|h1|h2|h3|h4|h5|h6|header|hr|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|main|map|mark|meter|nav|object|ol|optgroup|option|output|p|param|pre|progress|q|rb|rp|rt|rtc|ruby|s|samp|section|select|small|source|span|strong|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|tr|track|u|ul|ut|var|video|wbr';
         $tagsRegEx = '
@@ -3463,212 +3365,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
     }
 
     /**
-     * Implements the TypoScript function "addParams"
-     *
-     * @param string $content The string with the HTML tag.
-     * @param array $conf The TypoScript configuration properties
-     * @return string The modified string
-     * @todo Make it XHTML compatible. Will not present "/>" endings of tags right now. Further getting the tagname might fail if it is not separated by a normal space from the attributes.
-     */
-    public function addParams($content, $conf)
-    {
-        // For XHTML compliance.
-        $lowerCaseAttributes = true;
-        if (!is_array($conf)) {
-            return $content;
-        }
-        $key = 1;
-        $parts = explode('<', $content);
-        if ((int)$conf['_offset']) {
-            $key = (int)$conf['_offset'] < 0 ? count($parts) + (int)$conf['_offset'] : (int)$conf['_offset'];
-        }
-        $subparts = explode('>', $parts[$key]);
-        if (trim($subparts[0])) {
-            // Get attributes and name
-            $attribs = GeneralUtility::get_tag_attributes('<' . $subparts[0] . '>');
-            list($tagName) = explode(' ', $subparts[0], 2);
-            // adds/overrides attributes
-            foreach ($conf as $pkey => $val) {
-                if (substr($pkey, -1) !== '.' && $pkey[0] !== '_') {
-                    $tmpVal = isset($conf[$pkey . '.']) ? $this->stdWrap($conf[$pkey], $conf[$pkey . '.']) : (string)$val;
-                    if ($lowerCaseAttributes) {
-                        $pkey = strtolower($pkey);
-                    }
-                    if ($tmpVal !== '') {
-                        $attribs[$pkey] = $tmpVal;
-                    }
-                }
-            }
-            // Re-assembles the tag and content
-            $subparts[0] = trim($tagName . ' ' . GeneralUtility::implodeAttributes($attribs));
-            $parts[$key] = implode('>', $subparts);
-            $content = implode('<', $parts);
-        }
-        return $content;
-    }
-
-    /**
-     * Creates a list of links to files.
-     * Implements the stdWrap property "filelink"
-     *
-     * @param string $theValue The filename to link to, possibly prefixed with $conf[path]
-     * @param array $conf TypoScript parameters for the TypoScript function ->filelink
-     * @return string The link to the file possibly with icons, thumbnails, size in bytes shown etc.
-     * @access private
-     * @see stdWrap()
-     */
-    public function filelink($theValue, $conf)
-    {
-        $conf['path'] = isset($conf['path.']) ? $this->stdWrap($conf['path'], $conf['path.']) : $conf['path'];
-        $theFile = trim($conf['path']) . $theValue;
-        if (!@is_file($theFile)) {
-            return '';
-        }
-        $theFileEnc = str_replace('%2F', '/', rawurlencode($theFile));
-        $title = $conf['title'];
-        if (isset($conf['title.'])) {
-            $title = $this->stdWrap($title, $conf['title.']);
-        }
-        $target = $conf['target'];
-        if (isset($conf['target.'])) {
-            $target = $this->stdWrap($target, $conf['target.']);
-        }
-        $tsfe = $this->getTypoScriptFrontendController();
-
-        $typoLinkConf = [
-            'parameter' => $theFileEnc,
-            'fileTarget' => $target,
-            'title' => $title,
-            'ATagParams' => $this->getATagParams($conf)
-        ];
-
-        if (isset($conf['typolinkConfiguration.'])) {
-            $additionalTypoLinkConfiguration = $conf['typolinkConfiguration.'];
-            // We only allow additional configuration. This is why the generated conf overwrites the additional conf.
-            ArrayUtility::mergeRecursiveWithOverrule($additionalTypoLinkConfiguration, $typoLinkConf);
-            $typoLinkConf = $additionalTypoLinkConfiguration;
-        }
-
-        $theLinkWrap = $this->typoLink('|', $typoLinkConf);
-        $theSize = filesize($theFile);
-        $fI = GeneralUtility::split_fileref($theFile);
-        $icon = '';
-        if ($conf['icon']) {
-            $conf['icon.']['path'] = isset($conf['icon.']['path.'])
-                ? $this->stdWrap($conf['icon.']['path'], $conf['icon.']['path.'])
-                : $conf['icon.']['path'];
-            $iconPath = !empty($conf['icon.']['path'])
-                ? $conf['icon.']['path']
-                : GeneralUtility::getFileAbsFileName('EXT:frontend/Resources/Public/Icons/FileIcons/');
-            $conf['icon.']['ext'] = isset($conf['icon.']['ext.'])
-                ? $this->stdWrap($conf['icon.']['ext'], $conf['icon.']['ext.'])
-                : $conf['icon.']['ext'];
-            $iconExt = !empty($conf['icon.']['ext']) ? '.' . $conf['icon.']['ext'] : '.gif';
-            $icon = @is_file($iconPath . $fI['fileext'] . $iconExt)
-                ? $iconPath . $fI['fileext'] . $iconExt
-                : $iconPath . 'default' . $iconExt;
-            $icon = PathUtility::stripPathSitePrefix($icon);
-            // Checking for images: If image, then return link to thumbnail.
-            $IEList = isset($conf['icon_image_ext_list.']) ? $this->stdWrap($conf['icon_image_ext_list'], $conf['icon_image_ext_list.']) : $conf['icon_image_ext_list'];
-            $image_ext_list = str_replace(' ', '', strtolower($IEList));
-            if ($fI['fileext'] && GeneralUtility::inList($image_ext_list, $fI['fileext'])) {
-                if ($conf['iconCObject']) {
-                    $icon = $this->cObjGetSingle($conf['iconCObject'], $conf['iconCObject.'], 'iconCObject');
-                } else {
-                    $notFoundThumb = GeneralUtility::getFileAbsFileName('EXT:core/Resources/Public/Images/NotFound.gif');
-                    $notFoundThumb = PathUtility::stripPathSitePrefix($notFoundThumb);
-                    $sizeParts = [64, 64];
-                    if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails']) {
-                        // using the File Abstraction Layer to generate a preview image
-                        try {
-                            /** @var File $fileObject */
-                            $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($theFile);
-                            if ($fileObject->isMissing()) {
-                                $icon = $notFoundThumb;
-                            } else {
-                                $fileExtension = $fileObject->getExtension();
-                                if ($fileExtension === 'ttf' || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
-                                    if ($conf['icon_thumbSize'] || $conf['icon_thumbSize.']) {
-                                        $thumbSize = isset($conf['icon_thumbSize.']) ? $this->stdWrap($conf['icon_thumbSize'], $conf['icon_thumbSize.']) : $conf['icon_thumbSize'];
-                                        $sizeParts = explode('x', $thumbSize);
-                                    }
-                                    $icon = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, [
-                                        'width' => $sizeParts[0],
-                                        'height' => $sizeParts[1]
-                                    ])->getPublicUrl(true);
-                                }
-                            }
-                        } catch (ResourceDoesNotExistException $exception) {
-                            $icon = $notFoundThumb;
-                        }
-                    } else {
-                        $icon = $notFoundThumb;
-                    }
-                    $urlPrefix = '';
-                    if (parse_url($icon, PHP_URL_HOST) === null) {
-                        $urlPrefix = $tsfe->absRefPrefix;
-                    }
-                    $icon = '<img src="' . htmlspecialchars($urlPrefix . $icon) . '"' .
-                        ' width="' . (int)$sizeParts[0] . '" height="' . (int)$sizeParts[1] . '" ' .
-                        $this->getBorderAttr(' border="0"') . '' . $this->getAltParam($conf) . ' />';
-                }
-            } else {
-                $conf['icon.']['widthAttribute'] = isset($conf['icon.']['widthAttribute.'])
-                    ? $this->stdWrap($conf['icon.']['widthAttribute'], $conf['icon.']['widthAttribute.'])
-                    : $conf['icon.']['widthAttribute'];
-                $iconWidth = !empty($conf['icon.']['widthAttribute']) ? $conf['icon.']['widthAttribute'] : 18;
-                $conf['icon.']['heightAttribute'] = isset($conf['icon.']['heightAttribute.'])
-                    ? $this->stdWrap($conf['icon.']['heightAttribute'], $conf['icon.']['heightAttribute.'])
-                    : $conf['icon.']['heightAttribute'];
-                $iconHeight = !empty($conf['icon.']['heightAttribute']) ? (int)$conf['icon.']['heightAttribute'] : 16;
-                $icon = '<img src="' . htmlspecialchars($tsfe->absRefPrefix . $icon) . '" width="' . (int)$iconWidth . '" height="' . (int)$iconHeight . '"'
-                    . $this->getBorderAttr(' border="0"') . $this->getAltParam($conf) . ' />';
-            }
-            if ($conf['icon_link'] && !$conf['combinedLink']) {
-                $icon = $this->wrap($icon, $theLinkWrap);
-            }
-            $icon = isset($conf['icon.']) ? $this->stdWrap($icon, $conf['icon.']) : $icon;
-        }
-        $size = '';
-        if ($conf['size']) {
-            $size = isset($conf['size.']) ? $this->stdWrap($theSize, $conf['size.']) : $theSize;
-        }
-        // Wrapping file label
-        if ($conf['removePrependedNumbers']) {
-            $theValue = preg_replace('/_[0-9][0-9](\\.[[:alnum:]]*)$/', '\\1', $theValue);
-        }
-        if (isset($conf['labelStdWrap.'])) {
-            $theValue = $this->stdWrap($theValue, $conf['labelStdWrap.']);
-        }
-        // Wrapping file
-        $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
-        if ($conf['combinedLink']) {
-            $theValue = $icon . $theValue;
-            if ($conf['ATagBeforeWrap']) {
-                $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
-            } else {
-                $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
-            }
-            $file = isset($conf['file.']) ? $this->stdWrap($theValue, $conf['file.']) : $theValue;
-            // output
-            $output = $file . $size;
-        } else {
-            if ($conf['ATagBeforeWrap']) {
-                $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
-            } else {
-                $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
-            }
-            $file = isset($conf['file.']) ? $this->stdWrap($theValue, $conf['file.']) : $theValue;
-            // output
-            $output = $icon . $file . $size;
-        }
-        if (isset($conf['stdWrap.'])) {
-            $output = $this->stdWrap($output, $conf['stdWrap.']);
-        }
-        return $output;
-    }
-
-    /**
      * Performs basic mathematical evaluation of the input string. Does NOT take parathesis and operator precedence into account! (for that, see \TYPO3\CMS\Core\Utility\MathUtility::calculateWithPriorityToAdditionAndSubtraction())
      *
      * @param string $val The string to evaluate. Example: "3+4*10/5" will generate "35". Only integer numbers can be used.
@@ -3706,26 +3402,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
     }
 
     /**
-     * This explodes a comma-list into an array where the values are parsed through ContentObjectRender::calc() and cast to (int)(so you are sure to have integers in the output array)
-     * Used to split and calculate min and max values for GMENUs.
-     *
-     * @param string $delim Delimited to explode by
-     * @param string $string The string with parts in (where each part is evaluated by ->calc())
-     * @return array And array with evaluated values.
-     * @see calc(), \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject::makeGifs()
-     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10. It is solely used in GMENU, which can be handled there directly.
-     */
-    public function calcIntExplode($delim, $string)
-    {
-        trigger_error('calcIntExplode will be removed in TYPO3 v10.', E_USER_DEPRECATED);
-        $temp = explode($delim, $string);
-        foreach ($temp as $key => $val) {
-            $temp[$key] = (int)$this->calc($val);
-        }
-        return $temp;
-    }
-
-    /**
      * Implements the "split" property of stdWrap; Splits a string based on a token (given in TypoScript properties), sets the "current" value to each part and then renders a content object pointer to by a number.
      * In classic TypoScript (like 'content (default)'/'styles.content (default)') this is used to render tables, splitting rows and cells by tokens and putting them together again wrapped in <td> tags etc.
      * Implements the "optionSplit" processing of the TypoScript options for each splitted value to parse.
@@ -3733,7 +3409,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $value The string value to explode by $conf[token] and process each part
      * @param array $conf TypoScript properties for "split
      * @return string Compiled result
-     * @access private
+     * @internal
      * @see stdWrap(), \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject::procesItemStates()
      */
     public function splitObj($value, $conf)
@@ -3745,7 +3421,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
         $valArr = explode($conf['token'], $value);
 
         // return value directly by returnKey. No further processing
-        if (!empty($valArr) && (MathUtility::canBeInterpretedAsInteger($conf['returnKey']) || $conf['returnKey.'])) {
+        if (!empty($valArr) && (MathUtility::canBeInterpretedAsInteger($conf['returnKey'] ?? null) || ($conf['returnKey.'] ?? false))
+        ) {
             $key = isset($conf['returnKey.']) ? (int)$this->stdWrap($conf['returnKey'], $conf['returnKey.']) : (int)$conf['returnKey'];
             return $valArr[$key] ?? '';
         }
@@ -3827,9 +3504,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
         if ((isset($configuration['search']) || isset($configuration['search.'])) && (isset($configuration['replace']) || isset($configuration['replace.']))) {
             // Gets the strings
             $search = isset($configuration['search.']) ? $this->stdWrap($configuration['search'], $configuration['search.']) : $configuration['search'];
-            $replace = isset($configuration['replace.']) ? $this->stdWrap($configuration['replace'], $configuration['replace.']) : $configuration['replace'];
+            $replace = isset($configuration['replace.'])
+                ? $this->stdWrap($configuration['replace'] ?? null, $configuration['replace.'])
+                : $configuration['replace'] ?? null;
             // Determines whether regular expression shall be used
-            if (isset($configuration['useRegExp']) || $configuration['useRegExp.']) {
+            if (isset($configuration['useRegExp'])
+                || (isset($configuration['useRegExp.']) && $configuration['useRegExp.'])
+            ) {
                 $useRegularExpression = isset($configuration['useRegExp.']) ? $this->stdWrap($configuration['useRegExp'], $configuration['useRegExp.']) : $configuration['useRegExp'];
             }
             // Determines whether replace-pattern uses option-split
@@ -3897,8 +3578,12 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     protected function round($content, array $conf = [])
     {
-        $decimals = isset($conf['decimals.']) ? $this->stdWrap($conf['decimals'], $conf['decimals.']) : $conf['decimals'];
-        $type = isset($conf['roundType.']) ? $this->stdWrap($conf['roundType'], $conf['roundType.']) : $conf['roundType'];
+        $decimals = isset($conf['decimals.'])
+            ? $this->stdWrap($conf['decimals'] ?? '', $conf['decimals.'])
+            : ($conf['decimals'] ?? null);
+        $type = isset($conf['roundType.'])
+            ? $this->stdWrap($conf['roundType'] ?? '', $conf['roundType.'])
+            : ($conf['roundType'] ?? null);
         $floatVal = (float)$content;
         switch ($type) {
             case 'ceil':
@@ -3925,9 +3610,15 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function numberFormat($content, $conf)
     {
-        $decimals = isset($conf['decimals.']) ? (int)$this->stdWrap($conf['decimals'], $conf['decimals.']) : (int)$conf['decimals'];
-        $dec_point = isset($conf['dec_point.']) ? $this->stdWrap($conf['dec_point'], $conf['dec_point.']) : $conf['dec_point'];
-        $thousands_sep = isset($conf['thousands_sep.']) ? $this->stdWrap($conf['thousands_sep'], $conf['thousands_sep.']) : $conf['thousands_sep'];
+        $decimals = isset($conf['decimals.'])
+            ? (int)$this->stdWrap($conf['decimals'] ?? '', $conf['decimals.'])
+            : (int)($conf['decimals'] ?? 0);
+        $dec_point = isset($conf['dec_point.'])
+            ? $this->stdWrap($conf['dec_point'] ?? '', $conf['dec_point.'])
+            : ($conf['dec_point'] ?? null);
+        $thousands_sep = isset($conf['thousands_sep.'])
+            ? $this->stdWrap($conf['thousands_sep'] ?? '', $conf['thousands_sep.'])
+            : ($conf['thousands_sep'] ?? null);
         return number_format((float)$content, $decimals, $dec_point, $thousands_sep);
     }
 
@@ -3962,7 +3653,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $conf = $temp_conf['parseFunc.'];
         }
         // Process:
-        if ((string)$conf['externalBlocks'] === '') {
+        if ((string)($conf['externalBlocks'] ?? '') === '') {
             return $this->_parseFunc($theValue, $conf);
         }
         $tags = strtolower(implode(',', GeneralUtility::trimExplode(',', $conf['externalBlocks'])));
@@ -4052,7 +3743,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $theValue The value to process.
      * @param array $conf TypoScript configuration for parseFunc
      * @return string The processed value
-     * @access private
+     * @internal
      * @see parseFunc()
      */
     public function _parseFunc($theValue, $conf)
@@ -4069,8 +3760,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
         $stripNL = 0;
         $contentAccum = [];
         $contentAccumP = 0;
-        $allowTags = strtolower(str_replace(' ', '', $conf['allowTags']));
-        $denyTags = strtolower(str_replace(' ', '', $conf['denyTags']));
+        $allowTags = strtolower(str_replace(' ', '', $conf['allowTags'] ?? ''));
+        $denyTags = strtolower(str_replace(' ', '', $conf['denyTags'] ?? ''));
         $totalLen = strlen($theValue);
         do {
             if (!$inside) {
@@ -4106,7 +3797,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     if (!is_array($currentTag)) {
                         // Constants
                         $tsfe = $this->getTypoScriptFrontendController();
-                        $tmpConstants = $tsfe->tmpl->setup['constants.'];
+                        $tmpConstants = $tsfe->tmpl->setup['constants.'] ?? null;
                         if ($conf['constants'] && is_array($tmpConstants)) {
                             foreach ($tmpConstants as $key => $val) {
                                 if (is_string($val)) {
@@ -4115,7 +3806,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                             }
                         }
                         // Short
-                        if (is_array($conf['short.'])) {
+                        if (isset($conf['short.']) && is_array($conf['short.'])) {
                             $shortWords = $conf['short.'];
                             krsort($shortWords);
                             foreach ($shortWords as $key => $val) {
@@ -4125,17 +3816,17 @@ class ContentObjectRenderer implements LoggerAwareInterface
                             }
                         }
                         // stdWrap
-                        if (is_array($conf['plainTextStdWrap.'])) {
+                        if (isset($conf['plainTextStdWrap.']) && is_array($conf['plainTextStdWrap.'])) {
                             $data = $this->stdWrap($data, $conf['plainTextStdWrap.']);
                         }
                         // userFunc
-                        if ($conf['userFunc']) {
+                        if ($conf['userFunc'] ?? false) {
                             $data = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $data);
                         }
                         // Makelinks: (Before search-words as we need the links to be generated when searchwords go on...!)
-                        if ($conf['makelinks']) {
+                        if ($conf['makelinks'] ?? false) {
                             $data = $this->http_makelinks($data, $conf['makelinks.']['http.']);
-                            $data = $this->mailto_makelinks($data, $conf['makelinks.']['mailto.']);
+                            $data = $this->mailto_makelinks($data, $conf['makelinks.']['mailto.'] ?? []);
                         }
                         // Search Words:
                         if ($tsfe->no_cache && $conf['sword'] && is_array($tsfe->sWordList) && $tsfe->sWordRegEx) {
@@ -4168,7 +3859,9 @@ class ContentObjectRenderer implements LoggerAwareInterface
                             $data = $newstring;
                         }
                     }
-                    $contentAccum[$contentAccumP] .= $data;
+                    $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
+                        ? $contentAccum[$contentAccumP] . $data
+                        : $data;
                 }
                 $inside = true;
             } else {
@@ -4186,11 +3879,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     $tag[0] = substr($tag[0], 1);
                     $tag['out'] = 1;
                 }
-                if ($conf['tags.'][$tag[0]]) {
+                if ($conf['tags.'][$tag[0]] ?? false) {
                     $treated = false;
                     $stripNL = false;
                     // in-tag
-                    if (!$currentTag && !$tag['out']) {
+                    if (!$currentTag && (!isset($tag['out']) || !$tag['out'])) {
                         // $currentTag (array!) is the tag we are currently processing
                         $currentTag = $tag;
                         $contentAccumP++;
@@ -4201,13 +3894,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
                         }
                     }
                     // out-tag
-                    if ($currentTag[0] === $tag[0] && $tag['out']) {
+                    if ($currentTag[0] === $tag[0] && isset($tag['out']) && $tag['out']) {
                         $theName = $conf['tags.'][$tag[0]];
                         $theConf = $conf['tags.'][$tag[0] . '.'];
                         // This flag indicates, that NL- (13-10-chars) should be stripped first and last.
-                        $stripNL = (bool)$theConf['stripNL'];
+                        $stripNL = (bool)($theConf['stripNL'] ?? false);
                         // This flag indicates, that this TypoTag section should NOT be included in the nonTypoTag content.
-                        $breakOut = (bool)$theConf['breakoutTypoTagContent'];
+                        $breakOut = (bool)($theConf['breakoutTypoTagContent'] ?? false);
                         $this->parameters = [];
                         if ($currentTag[1]) {
                             $params = GeneralUtility::get_tag_attributes($currentTag[1]);
@@ -4232,7 +3925,10 @@ class ContentObjectRenderer implements LoggerAwareInterface
                         $contentAccumP++;
                         // If the TypoTag section
                         if (!$breakOut) {
-                            $contentAccum[$contentAccumP - 2] .= $contentAccum[$contentAccumP - 1] . $contentAccum[$contentAccumP];
+                            if (!isset($contentAccum[$contentAccumP - 2])) {
+                                $contentAccum[$contentAccumP - 2] = '';
+                            }
+                            $contentAccum[$contentAccumP - 2] .= ($contentAccum[$contentAccumP - 1] ?? '') . ($contentAccum[$contentAccumP] ?? '');
                             unset($contentAccum[$contentAccumP]);
                             unset($contentAccum[$contentAccumP - 1]);
                             $contentAccumP -= 2;
@@ -4248,9 +3944,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     // If a tag was not a typo tag, then it is just added to the content
                     $stripNL = false;
                     if (GeneralUtility::inList($allowTags, $tag[0]) || $denyTags !== '*' && !GeneralUtility::inList($denyTags, $tag[0])) {
-                        $contentAccum[$contentAccumP] .= $data;
+                        $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
+                            ? $contentAccum[$contentAccumP] . $data
+                            : $data;
                     } else {
-                        $contentAccum[$contentAccumP] .= htmlspecialchars($data);
+                        $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
+                            ? $contentAccum[$contentAccumP] . htmlspecialchars($data)
+                            : htmlspecialchars($data);
                     }
                 }
                 $inside = false;
@@ -4263,11 +3963,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
         for ($a = 0; $a < $contentAccumCount; $a++) {
             if ($a % 2 != 1) {
                 // stdWrap
-                if (is_array($conf['nonTypoTagStdWrap.'])) {
+                if (isset($conf['nonTypoTagStdWrap.']) && is_array($conf['nonTypoTagStdWrap.'])) {
                     $contentAccum[$a] = $this->stdWrap($contentAccum[$a], $conf['nonTypoTagStdWrap.']);
                 }
                 // userFunc
-                if ($conf['nonTypoTagUserFunc']) {
+                if (!empty($conf['nonTypoTagUserFunc'])) {
                     $contentAccum[$a] = $this->callUserFunction($conf['nonTypoTagUserFunc'], $conf['nonTypoTagUserFunc.'], $contentAccum[$a]);
                 }
             }
@@ -4281,7 +3981,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $theValue The input value
      * @param array $conf TypoScript options
      * @return string The processed input value being returned; Splitted lines imploded by LF again.
-     * @access private
+     * @internal
      */
     public function encaps_lineSplit($theValue, $conf)
     {
@@ -4299,8 +3999,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
         $encapTags = GeneralUtility::trimExplode(',', strtolower($conf['encapsTagList']), true);
         $nonWrappedTag = $conf['nonWrappedTag'];
         $defaultAlign = isset($conf['defaultAlign.'])
-            ? trim($this->stdWrap($conf['defaultAlign'], $conf['defaultAlign.']))
-            : trim($conf['defaultAlign']);
+            ? trim($this->stdWrap($conf['defaultAlign'] ?? '', $conf['defaultAlign.']))
+            : trim($conf['defaultAlign'] ?? '');
 
         $str_content = '';
         foreach ($lParts as $k => $l) {
@@ -4310,7 +4010,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $attrib = [];
             $nonWrapped = false;
             $tagName = '';
-            if ($l[0] === '<' && substr($l, -1) === '>') {
+            if (isset($l[0]) && $l[0] === '<' && substr($l, -1) === '>') {
                 $fwParts = explode('>', substr($l, 1), 2);
                 list($tagName) = explode(' ', $fwParts[0], 2);
                 if (!$fwParts[1]) {
@@ -4331,7 +4031,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
             }
             if ($sameBeginEnd && in_array(strtolower($tagName), $encapTags)) {
                 $uTagName = strtoupper($tagName);
-                $uTagName = strtoupper($conf['remapTag.'][$uTagName] ? $conf['remapTag.'][$uTagName] : $uTagName);
+                $uTagName = strtoupper($conf['remapTag.'][$uTagName] ?? $uTagName);
             } else {
                 $uTagName = strtoupper($nonWrappedTag);
                 // The line will be wrapped: $uTagName should not be an empty tag
@@ -4346,11 +4046,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
             }
             if ($uTagName) {
                 // Setting common attributes
-                if (is_array($conf['addAttributes.'][$uTagName . '.'])) {
+                if (isset($conf['addAttributes.'][$uTagName . '.']) && is_array($conf['addAttributes.'][$uTagName . '.'])) {
                     foreach ($conf['addAttributes.'][$uTagName . '.'] as $kk => $vv) {
                         if (!is_array($vv)) {
                             if ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'blank') {
-                                if ((string)$attrib[$kk] === '') {
+                                if ((string)($attrib[$kk] ?? '') === '') {
                                     $attrib[$kk] = $vv;
                                 }
                             } elseif ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'exists') {
@@ -4364,15 +4064,15 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     }
                 }
                 // Wrapping all inner-content:
-                if (is_array($conf['encapsLinesStdWrap.'][$uTagName . '.'])) {
+                if (isset($conf['encapsLinesStdWrap.'][$uTagName . '.']) && is_array($conf['encapsLinesStdWrap.'][$uTagName . '.'])) {
                     $str_content = $this->stdWrap($str_content, $conf['encapsLinesStdWrap.'][$uTagName . '.']);
                 }
                 // Default align
-                if (!$attrib['align'] && $defaultAlign) {
+                if ((!isset($attrib['align']) || !$attrib['align']) && $defaultAlign) {
                     $attrib['align'] = $defaultAlign;
                 }
                 $params = GeneralUtility::implodeAttributes($attrib, true);
-                if (!$conf['removeWrapping'] || ($emptyTag && $conf['removeWrapping.']['keepSingleTag'])) {
+                if (!isset($conf['removeWrapping']) || !$conf['removeWrapping'] || ($emptyTag && $conf['removeWrapping.']['keepSingleTag'])) {
                     $selfClosingTagList = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
                     if ($emptyTag && in_array(strtolower($uTagName), $selfClosingTagList, true)) {
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . ' />';
@@ -4381,7 +4081,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     }
                 }
             }
-            if ($nonWrapped && $conf['wrapNonWrappedLines']) {
+            if ($nonWrapped && isset($conf['wrapNonWrappedLines']) && $conf['wrapNonWrappedLines']) {
                 $str_content = $this->wrap($str_content, $conf['wrapNonWrappedLines']);
             }
             $lParts[$k] = $str_content;
@@ -4544,9 +4244,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
         }
         $imageResource = null;
         if ($file === 'GIFBUILDER') {
-            /** @var GifBuilder $gifCreator */
             $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
-            $gifCreator->init();
             $theImage = '';
             if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
                 $gifCreator->start($fileArray, $this->data);
@@ -4561,7 +4259,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                 $fileObject = $file->getOriginalFile();
             } else {
                 try {
-                    if ($fileArray['import.']) {
+                    if (isset($fileArray['import.']) && $fileArray['import.']) {
                         $importedFile = trim($this->stdWrap('', $fileArray['import.']));
                         if (!empty($importedFile)) {
                             $file = $importedFile;
@@ -4631,8 +4329,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     $processedFileObject = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
                     if ($processedFileObject->isProcessed()) {
                         $imageResource = [
-                            0 => $processedFileObject->getProperty('width'),
-                            1 => $processedFileObject->getProperty('height'),
+                            0 => (int)$processedFileObject->getProperty('width'),
+                            1 => (int)$processedFileObject->getProperty('height'),
                             2 => $processedFileObject->getExtension(),
                             3 => $processedFileObject->getPublicUrl(),
                             'origFile' => $fileObject->getPublicUrl(),
@@ -4651,9 +4349,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
         if (!isset($imageResource)) {
             try {
                 $theImage = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize((string)$file);
-                $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
-                $gifCreator->init();
-                $info = $gifCreator->imageMagickConvert($theImage, 'WEB');
+                $info = GeneralUtility::makeInstance(GifBuilder::class)->imageMagickConvert($theImage, 'WEB');
                 $info['origFile'] = $theImage;
                 // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder, ln 100ff in order for the setup-array to create a unique filename hash.
                 $info['origFile_mtime'] = @filemtime($theImage);
@@ -4691,8 +4387,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     protected function getCropAreaFromFileReference(FileReference $fileReference, array $fileArray)
     {
-        /** @var Area $cropArea */
-        $cropArea = null;
         // Use cropping area from file reference if nothing is configured in TypoScript.
         if (!isset($fileArray['crop']) && !isset($fileArray['crop.'])) {
             // Set crop variant from TypoScript settings. If not set, use default.
@@ -4767,12 +4461,12 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * Returns the value for the field from $this->data. If "//" is found in the $field value that token will split the field values apart and the first field having a non-blank value will be returned.
      *
      * @param string $field The fieldname, eg. "title" or "navtitle // title" (in the latter case the value of $this->data[navtitle] is returned if not blank, otherwise $this->data[title] will be)
-     * @return string
+     * @return string|null
      */
     public function getFieldVal($field)
     {
         if (!strstr($field, '//')) {
-            return $this->data[trim($field)];
+            return $this->data[trim($field)] ?? null;
         }
         $sections = GeneralUtility::trimExplode('//', $field, true);
         foreach ($sections as $k) {
@@ -4807,7 +4501,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $parts = explode(':', $secVal, 2);
             $type = strtolower(trim($parts[0]));
             $typesWithOutParameters = ['level', 'date', 'current', 'pagelayout'];
-            $key = trim($parts[1]);
+            $key = trim($parts[1] ?? '');
             if (($key != '') || in_array($type, $typesWithOutParameters)) {
                 switch ($type) {
                     case 'gp':
@@ -4835,7 +4529,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                         $retVal = $this->parameters[$key];
                         break;
                     case 'register':
-                        $retVal = $tsfe->register[$key];
+                        $retVal = $tsfe->register[$key] ?? null;
                         break;
                     case 'global':
                         $retVal = $this->getGlobal($key);
@@ -4846,12 +4540,12 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     case 'leveltitle':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
-                        $retVal = $this->rootLineValue($numericKey, 'title', strtolower($keyParts[1]) === 'slide');
+                        $retVal = $this->rootLineValue($numericKey, 'title', strtolower($keyParts[1] ?? '') === 'slide');
                         break;
                     case 'levelmedia':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
-                        $retVal = $this->rootLineValue($numericKey, 'media', strtolower($keyParts[1]) === 'slide');
+                        $retVal = $this->rootLineValue($numericKey, 'media', strtolower($keyParts[1] ?? '') === 'slide');
                         break;
                     case 'leveluid':
                         $numericKey = $this->getKey($key, $tsfe->tmpl->rootLine);
@@ -4860,13 +4554,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     case 'levelfield':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
-                        $retVal = $this->rootLineValue($numericKey, $keyParts[1], strtolower($keyParts[2]) === 'slide');
+                        $retVal = $this->rootLineValue($numericKey, $keyParts[1], strtolower($keyParts[2] ?? '') === 'slide');
                         break;
                     case 'fullrootline':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $fullKey = (int)$keyParts[0] - count($tsfe->tmpl->rootLine) + count($tsfe->rootLine);
                         if ($fullKey >= 0) {
-                            $retVal = $this->rootLineValue($fullKey, $keyParts[1], stristr($keyParts[2], 'slide'), $tsfe->rootLine);
+                            $retVal = $this->rootLineValue($fullKey, $keyParts[1], stristr($keyParts[2] ?? '', 'slide'), $tsfe->rootLine);
                         }
                         break;
                     case 'date':
@@ -4885,7 +4579,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                         //   field = backend_layout
                         //   ifEmpty.data = levelfield:-2, backend_layout_next_level, slide
                         //   ifEmpty.ifEmpty = default
-                        $retVal = $GLOBALS['TSFE']->page['backend_layout'];
+                        $retVal = $tsfe->page['backend_layout'];
 
                         // If it is set to "none" - don't use any
                         if ($retVal === '-1') {
@@ -4914,7 +4608,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                         }
                         break;
                     case 'current':
-                        $retVal = $this->data[$this->currentValKey];
+                        $retVal = $this->data[$this->currentValKey] ?? null;
                         break;
                     case 'db':
                         $selectParts = GeneralUtility::trimExplode(':', $key);
@@ -5000,6 +4694,32 @@ class ContentObjectRenderer implements LoggerAwareInterface
                         if (!is_scalar($retVal)) {
                             $retVal = '';
                         }
+                        break;
+                    case 'site':
+                        $request = $GLOBALS['TYPO3_REQUEST'] ?? null;
+                        $site = $request ? $request->getAttribute('site') : null;
+                        if ($site instanceof Site) {
+                            if ($key === 'identifier') {
+                                $retVal = $site->getIdentifier();
+                            } else {
+                                try {
+                                    $retVal = ArrayUtility::getValueByPath($site->getConfiguration(), $key, '.');
+                                } catch (MissingArrayPathException $exception) {
+                                    $this->logger->warning(sprintf('getData() with "%s" failed', $key), ['exception' => $exception]);
+                                }
+                            }
+                        }
+                        break;
+                    case 'sitelanguage':
+                        $request = $GLOBALS['TYPO3_REQUEST'] ?? null;
+                        $siteLanguage = $request ? $request->getAttribute('language') : null;
+                        if ($siteLanguage instanceof SiteLanguage) {
+                            $config = $siteLanguage->toArray();
+                            if (isset($config[$key])) {
+                                $retVal = $config[$key];
+                            }
+                        }
+                        break;
                 }
             }
 
@@ -5086,7 +4806,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param bool $slideBack If set, then we will traverse through the rootline from outer level towards the root level until the value found is TRUE
      * @param mixed $altRootLine If you supply an array for this it will be used as an alternative root line array
      * @return string The value from the field of the rootline.
-     * @access private
+     * @internal
      * @see getData()
      */
     public function rootLineValue($key, $field, $slideBack = false, $altRootLine = '')
@@ -5144,7 +4864,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param int $key The integer to transform
      * @param array $arr array in which the key should be found.
      * @return int The processed integer key value.
-     * @access private
+     * @internal
      * @see getData()
      */
     public function getKey($key, $arr)
@@ -5189,8 +4909,12 @@ class ContentObjectRenderer implements LoggerAwareInterface
         $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($mixedLinkParameter);
 
         // Check for link-handler keyword
-        list($linkHandlerKeyword, $linkHandlerValue) = explode(':', $linkParameterParts['url'], 2);
-        if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword] && (string)$linkHandlerValue !== '') {
+        $linkHandlerExploded = explode(':', $linkParameterParts['url'], 2);
+        $linkHandlerKeyword = $linkHandlerExploded[0] ?? null;
+        $linkHandlerValue = $linkHandlerExploded[1] ?? null;
+        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword])
+            && (string)$linkHandlerValue !== ''
+        ) {
             $linkHandlerObj = GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]);
             if (method_exists($linkHandlerObj, 'main')) {
                 return $linkHandlerObj->main($linkText, $configuration, $linkHandlerKeyword, $linkHandlerValue, $mixedLinkParameter, $this);
@@ -5255,7 +4979,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
         $linkText = (string)$linkText;
         $tsfe = $this->getTypoScriptFrontendController();
 
-        $linkParameter = trim(isset($conf['parameter.']) ? $this->stdWrap($conf['parameter'], $conf['parameter.']) : $conf['parameter']);
+        $linkParameter = trim(
+            (isset($conf['parameter.']))
+            ? $this->stdWrap($conf['parameter'] ?? '', $conf['parameter.'])
+            : ($conf['parameter'] ?? '')
+        );
         $this->lastTypoLinkUrl = '';
         $this->lastTypoLinkTarget = '';
 
@@ -5286,7 +5014,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
             /** @var AbstractTypolinkBuilder $linkBuilder */
             $linkBuilder = GeneralUtility::makeInstance(
                 $GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']],
-                $this
+                $this,
+                $tsfe
             );
             try {
                 list($this->lastTypoLinkUrl, $linkText, $target) = $linkBuilder->build($linkDetails, $linkText, $target, $conf);
@@ -5322,8 +5051,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
 
         // Title attribute
         if (empty($title)) {
-            $title = $conf['title'];
-            if ($conf['title.']) {
+            $title = $conf['title'] ?? '';
+            if (isset($conf['title.']) && is_array($conf['title.'])) {
                 $title = $this->stdWrap($title, $conf['title.']);
             }
         }
@@ -5383,7 +5112,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
         $this->lastTypoLinkTarget = $target;
 
         // Call user function:
-        if ($conf['userFunc']) {
+        if ($conf['userFunc'] ?? false) {
             $finalTagParts['TAG'] = $finalAnchorTag;
             $finalAnchorTag = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $finalTagParts);
         }
@@ -5402,20 +5131,20 @@ class ContentObjectRenderer implements LoggerAwareInterface
         }
 
         // If flag "returnLastTypoLinkUrl" set, then just return the latest URL made:
-        if ($conf['returnLast']) {
+        if ($conf['returnLast'] ?? false) {
             switch ($conf['returnLast']) {
                 case 'url':
                     return $this->lastTypoLinkUrl;
-                    break;
                 case 'target':
                     return $this->lastTypoLinkTarget;
-                    break;
             }
         }
 
-        $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
+        $wrap = isset($conf['wrap.'])
+            ? $this->stdWrap($conf['wrap'] ?? '', $conf['wrap.'])
+            : $conf['wrap'] ?? '';
 
-        if ($conf['ATagBeforeWrap']) {
+        if ($conf['ATagBeforeWrap'] ?? false) {
             return $finalAnchorTag . $this->wrap($linkText, $wrap) . '</a>';
         }
         return $this->wrap($finalAnchorTag . $linkText . '</a>', $wrap);
@@ -5458,7 +5187,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
         }
         if (is_array($urlParameters)) {
             if (!empty($urlParameters)) {
-                $conf['additionalParams'] .= GeneralUtility::implodeArrayForUrl('', $urlParameters);
+                $conf['additionalParams'] .= HttpUtility::buildQueryString($urlParameters, '&');
             }
         } else {
             $conf['additionalParams'] .= $urlParameters;
@@ -5507,33 +5236,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
     }
 
     /**
-     * Generates a typolink and returns the two link tags - start and stop - in an array
-     *
-     * @param array $conf "typolink" TypoScript properties
-     * @return array An array with two values in key 0+1, each value being the start and close <a>-tag of the typolink properties being inputted in $conf
-     * @see typolink()
-     */
-    public function typolinkWrap($conf)
-    {
-        $k = md5(microtime());
-        return explode($k, $this->typoLink($k, $conf));
-    }
-
-    /**
-     * Returns the current page URL
-     *
-     * @param array|string $urlParameters As an array key/value pairs represent URL parameters to set. Values NOT URL-encoded yet, keys should be URL-encoded if needed. As a string the parameter is expected to be URL-encoded already.
-     * @param int $id An alternative ID to the current id ($GLOBALS['TSFE']->id)
-     * @return string The URL
-     * @see getTypoLink_URL()
-     */
-    public function currentPageUrl($urlParameters = [], $id = 0)
-    {
-        $tsfe = $this->getTypoScriptFrontendController();
-        return $this->getTypoLink_URL($id ?: $tsfe->id, $urlParameters, $tsfe->sPre);
-    }
-
-    /**
      * Loops over all configured URL modifier hooks (if available) and returns the generated URL or NULL if no URL was generated.
      *
      * @param string $context The context in which the method is called (e.g. typoLink).
@@ -5621,14 +5323,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param mixed  $type - either "ascii" or a number between -10 and 10, taken from config.spamProtectEmailAddresses
      * @return string encoded version of $string
      */
-    protected function encryptEmail($string, $type)
+    protected function encryptEmail(string $string, $type): string
     {
         $out = '';
         // obfuscates using the decimal HTML entity references for each character
         if ($type === 'ascii') {
-            $stringLength = strlen($string);
-            for ($a = 0; $a < $stringLength; $a++) {
-                $out .= '&#' . ord(substr($string, $a, 1)) . ';';
+            foreach (preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY) as $char) {
+                $out .= '&#' . mb_ord($char) . ';';
             }
         } else {
             // like str_rot13() but with a variable offset and a wider character range
@@ -5660,14 +5361,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param mixed  $type - either "ascii" or a number between -10 and 10 taken from config.spamProtectEmailAddresses
      * @return string decoded version of $string
      */
-    protected function decryptEmail($string, $type)
+    protected function decryptEmail(string $string, $type): string
     {
         $out = '';
         // obfuscates using the decimal HTML entity references for each character
         if ($type === 'ascii') {
-            $stringLength = strlen($string);
-            for ($a = 0; $a < $stringLength; $a++) {
-                $out .= '&#' . ord(substr($string, $a, 1)) . ';';
+            foreach (preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY) as $char) {
+                $out .= '&#' . mb_ord($char) . ';';
             }
         } else {
             // like str_rot13() but with a variable offset and a wider character range
@@ -5724,7 +5424,8 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function getQueryArguments($conf, $overruleQueryArguments = [], $forceOverruleArguments = false)
     {
-        switch ((string)$conf['method']) {
+        $method = (string)($conf['method'] ?? '');
+        switch ($method) {
             case 'GET':
                 $currentQueryArray = GeneralUtility::_GET();
                 break;
@@ -5743,7 +5444,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
                 $currentQueryArray = [];
                 parse_str($this->getEnvironmentVariable('QUERY_STRING'), $currentQueryArray);
         }
-        if ($conf['exclude']) {
+        if ($conf['exclude'] ?? false) {
             $excludeString = str_replace(',', '&', $conf['exclude']);
             $excludedQueryParts = [];
             parse_str($excludeString, $excludedQueryParts);
@@ -5754,7 +5455,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $newQueryArray = $currentQueryArray;
         }
         ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments, $forceOverruleArguments);
-        return GeneralUtility::implodeArrayForUrl('', $newQueryArray, '', false, true);
+        return HttpUtility::buildQueryString($newQueryArray, '&');
     }
 
     /***********************************************
@@ -5777,7 +5478,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
     {
         if ($wrap) {
             $wrapArr = explode($char, $wrap);
-            $content = trim($wrapArr[0]) . $content . trim($wrapArr[1]);
+            $content = trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
         }
         return $content;
     }
@@ -5961,7 +5662,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
         } else {
             $val = round($absSeconds / (365 * 24 * 3600));
-            $seconds = $sign * $val . ($val == 1 ? $labelArr[7] : $labelArr[3]);
+            $seconds = $sign * $val . ($val == 1 ? ($labelArr[7] ?? null) : ($labelArr[3] ?? null));
         }
         return $seconds;
     }
@@ -5979,7 +5680,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function sendNotifyEmail($message, $recipients, $cc, $senderAddress, $senderName = '', $replyTo = '')
     {
-        /** @var $mail MailMessage */
+        /** @var MailMessage $mail */
         $mail = GeneralUtility::makeInstance(MailMessage::class);
         $senderName = trim($senderName);
         $senderAddress = trim($senderAddress);
@@ -6008,7 +5709,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $parsedCc = MailUtility::parseAddresses($cc);
             if (!empty($parsedCc)) {
                 $from = $mail->getFrom();
-                /** @var $mail MailMessage */
+                /** @var MailMessage $mail */
                 $mail = GeneralUtility::makeInstance(MailMessage::class);
                 if (!empty($parsedReplyTo)) {
                     $mail->setReplyTo($parsedReplyTo);
@@ -6055,28 +5756,6 @@ class ContentObjectRenderer implements LoggerAwareInterface
      ***********************************************/
 
     /**
-     * Returns a part of a WHERE clause which will filter out records with start/end times or hidden/fe_groups fields
-     * set to values that should de-select them according to the current time, preview settings or user login.
-     * Definitely a frontend function.
-     * THIS IS A VERY IMPORTANT FUNCTION: Basically you must add the output from this function for EVERY select query you create
-     * for selecting records of tables in your own applications - thus they will always be filtered according to the "enablefields"
-     * configured in TCA
-     * Simply calls \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() BUT will send the show_hidden flag along!
-     * This means this function will work in conjunction with the preview facilities of the frontend engine/Admin Panel.
-     *
-     * @param string $table The table for which to get the where clause
-     * @param bool $show_hidden If set, then you want NOT to filter out hidden records. Otherwise hidden record are filtered based on the current preview settings.
-     * @param array $ignore_array Array you can pass where keys can be "disabled", "starttime", "endtime", "fe_group" (keys from "enablefields" in TCA) and if set they will make sure that part of the clause is not added. Thus disables the specific part of the clause. For previewing etc.
-     * @return string The part of the where clause on the form " AND [fieldname]=0 AND ...". Eg. " AND hidden=0 AND starttime < 123345567
-     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
-     */
-    public function enableFields($table, $show_hidden = false, array $ignore_array = [])
-    {
-        trigger_error('cObj->enableFields() will be removed in TYPO3 v10. should be used from the PageRepository->enableFields() functionality directly.', E_USER_DEPRECATED);
-        return $this->getTypoScriptFrontendController()->sys_page->enableFields($table, $show_hidden ? true : -1, $ignore_array);
-    }
-
-    /**
      * Generates a list of Page-uid's from $id. List does not include $id itself
      * (unless the id specified is negative in which case it does!)
      * The only pages WHICH PREVENTS DECENDING in a branch are
@@ -6194,7 +5873,9 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     $queryBuilder->expr()->eq(
                         'pid',
                         $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
-                    )
+                    ),
+                    // tree is only built by language=0 pages
+                    $queryBuilder->expr()->eq('sys_language_uid', 0)
                 )
                 ->orderBy('sorting');
 
@@ -6302,15 +5983,21 @@ class ContentObjectRenderer implements LoggerAwareInterface
                     $theList[] = $addId;
                 }
             }
-            GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('cache_treelist')->insert(
-                'cache_treelist',
-                [
-                    'md5hash' => $requestHash,
-                    'pid' => $id,
-                    'treelist' => implode(',', $theList),
-                    'tstamp' => $GLOBALS['EXEC_TIME']
-                ]
-            );
+
+            $cacheEntry = [
+                'md5hash' => $requestHash,
+                'pid' => $id,
+                'treelist' => implode(',', $theList),
+                'tstamp' => $GLOBALS['EXEC_TIME'],
+            ];
+
+            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('cache_treelist');
+            try {
+                $connection->transactional(function ($connection) use ($cacheEntry) {
+                    $connection->insert('cache_treelist', $cacheEntry);
+                });
+            } catch (\Throwable $e) {
+            }
         }
 
         return implode(',', $theList);
@@ -6421,7 +6108,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @return mixed A SELECT query if $returnQueryArray is FALSE, otherwise the SELECT query in an array as parts.
      * @throws \RuntimeException
      * @throws \InvalidArgumentException
-     * @access private
+     * @internal
      * @see CONTENT(), numRows()
      */
     public function getQuery($table, $conf, $returnQueryArray = false)
@@ -6649,9 +6336,9 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $knownAliases[$tableReference] = true;
 
             $fromClauses[$tableReference] = $tableSql . $this->getQueryArrayJoinHelper(
-                    $tableReference,
-                    $queryBuilder->getQueryPart('join'),
-                    $knownAliases
+                $tableReference,
+                $queryBuilder->getQueryPart('join'),
+                $knownAliases
                 );
         }
 
@@ -6789,42 +6476,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
             $constraints[] = QueryHelper::stripLogicalOperatorPrefix($where);
         }
 
-        // Check if the table is translatable, and set the language field by default from the TCA information
-        $languageField = '';
-        if (!empty($conf['languageField']) || !isset($conf['languageField'])) {
-            if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) {
-                $languageField = $conf['languageField'];
-            } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
-                $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
-            }
-        }
-
-        if (!empty($languageField)) {
-            // The sys_language record UID of the content of the page
-            /** @var LanguageAspect $languageAspect */
-            $languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
-
-            if ($languageAspect->doOverlays() && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
-                // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will
-                // OVERLAY the records with localized versions!
-                $languageQuery = $expressionBuilder->in($languageField, [0, -1]);
-                // Use this option to include records that don't have a default translation
-                // (originalpointerfield is 0 and the language field contains the requested language)
-                $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ?
-                    $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation'];
-                if (trim($includeRecordsWithoutDefaultTranslation) !== '') {
-                    $languageQuery = $expressionBuilder->orX(
-                        $languageQuery,
-                        $expressionBuilder->andX(
-                            $expressionBuilder->eq($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], 0),
-                            $expressionBuilder->eq($languageField, $languageAspect->getContentId())
-                        )
-                    );
-                }
-            } else {
-                $languageQuery = $expressionBuilder->eq($languageField, $languageAspect->getContentId());
-            }
-            $constraints[] = $languageQuery;
+        // Check if the default language should be fetched (= doing overlays), or if only the records of a language should be fetched
+        // but only do this for TCA tables that have languages enabled
+        $languageConstraint = $this->getLanguageRestriction($expressionBuilder, $table, $conf, GeneralUtility::makeInstance(Context::class));
+        if ($languageConstraint !== null) {
+            $constraints[] = $languageConstraint;
         }
 
         // Enablefields
@@ -6861,6 +6517,81 @@ class ContentObjectRenderer implements LoggerAwareInterface
     }
 
     /**
+     * Adds parts to the WHERE clause that are related to language.
+     * This only works on TCA tables which have the [ctrl][languageField] field set or if they
+     * have select.languageField = my_language_field set explicitly.
+     *
+     * It is also possible to disable the language restriction for a query by using select.languageField = 0,
+     * if select.languageField is not explicitly set, the TCA default values are taken.
+     *
+     * If the table is "localizeable" (= any of the criteria above is met), then the DB query is restricted:
+     *
+     * If the current language aspect has overlays enabled, then the only records with language "0" or "-1" are
+     * fetched (the overlays are taken care of later-on).
+     * if the current language has overlays but also records without localization-parent (free mode) available,
+     * then these are fetched as well. This can explicitly set via select.includeRecordsWithoutDefaultTranslation = 1
+     * which overrules the overlayType within the language aspect.
+     *
+     * If the language aspect has NO overlays enabled, it behaves as in "free mode" (= only fetch the records
+     * for the current language.
+     *
+     * @param ExpressionBuilder $expressionBuilder
+     * @param string $table
+     * @param array $conf
+     * @param Context $context
+     * @return string|\TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression|null
+     * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
+     */
+    protected function getLanguageRestriction(ExpressionBuilder $expressionBuilder, string $table, array $conf, Context $context)
+    {
+        $languageField = '';
+        $localizationParentField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? null;
+        // Check if the table is translatable, and set the language field by default from the TCA information
+        if (!empty($conf['languageField']) || !isset($conf['languageField'])) {
+            if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) {
+                $languageField = $conf['languageField'];
+            } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($localizationParentField)) {
+                $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
+            }
+        }
+
+        // No language restriction enabled explicitly or available via TCA
+        if (empty($languageField)) {
+            return null;
+        }
+
+        /** @var LanguageAspect $languageAspect */
+        $languageAspect = $context->getAspect('language');
+        if ($languageAspect->doOverlays() && !empty($localizationParentField)) {
+            // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will
+            // OVERLAY the records with localized versions!
+            $languageQuery = $expressionBuilder->in($languageField, [0, -1]);
+            // Use this option to include records that don't have a default translation ("free mode")
+            // (originalpointerfield is 0 and the language field contains the requested language)
+            if (isset($conf['includeRecordsWithoutDefaultTranslation']) || $conf['includeRecordsWithoutDefaultTranslation.']) {
+                $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ?
+                    $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation'];
+                $includeRecordsWithoutDefaultTranslation = trim($includeRecordsWithoutDefaultTranslation) !== '';
+            } else {
+                // Option was not explicitly set, check what's in for the language overlay type.
+                $includeRecordsWithoutDefaultTranslation = $languageAspect->getOverlayType() === $languageAspect::OVERLAYS_ON_WITH_FLOATING;
+            }
+            if ($includeRecordsWithoutDefaultTranslation) {
+                $languageQuery = $expressionBuilder->orX(
+                    $languageQuery,
+                    $expressionBuilder->andX(
+                        $expressionBuilder->eq($localizationParentField, 0),
+                        $expressionBuilder->eq($languageField, $languageAspect->getContentId())
+                    )
+                );
+            }
+            return $languageQuery;
+        }
+        // No overlays = only fetch records given for the requested language and "all languages"
+        return $expressionBuilder->in($languageField, [$languageAspect->getContentId(), -1]);
+    }
+
+    /**
      * Helper function for getQuery, sanitizing the select part
      *
      * This functions checks if the necessary fields are part of the select
@@ -6869,7 +6600,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $selectPart Select part
      * @param string $table Table to select from
      * @return string Sanitized select part
-     * @access private
+     * @internal
      * @see getQuery
      */
     protected function sanitizeSelectPart($selectPart, $table)
@@ -6905,7 +6636,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      *
      * @param array $listArr Array of Page UID numbers for select and for which pages with enablefields and bad doktypes should be removed.
      * @return array Returns the array of remaining page UID numbers
-     * @access private
+     * @internal
      * @see checkPid()
      */
     public function checkPidArray($listArr)
@@ -6948,7 +6679,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      *
      * @param int $uid Page UID to test
      * @return bool TRUE if OK
-     * @access private
+     * @internal
      * @see checkPidArray()
      */
     public function checkPid($uid)
@@ -6987,7 +6718,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $table Table to select records from
      * @param array $conf Select part of CONTENT definition
      * @return array List of values to replace markers with
-     * @access private
+     * @internal
      * @see getQuery()
      */
     public function getQueryMarkers($table, $conf)
@@ -7069,20 +6800,57 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $content A content string containing the content related to the edit panel. For cObject "EDITPANEL" this is empty but not so for the stdWrap property. The edit panel is appended to this string and returned.
      * @param array $conf TypoScript configuration properties for the editPanel
      * @param string $currentRecord The "table:uid" of the record being shown. If empty string then $this->currentRecord is used. For new records (set by $conf['newRecordFromTable']) it's auto-generated to "[tablename]:NEW
-     * @param array $dataArr Alternative data array to use. Default is $this->data
+     * @param array $dataArray Alternative data array to use. Default is $this->data
      * @return string The input content string with the editPanel appended. This function returns only an edit panel appended to the content string if a backend user is logged in (and has the correct permissions). Otherwise the content string is directly returned.
      */
-    public function editPanel($content, $conf, $currentRecord = '', $dataArr = [])
+    public function editPanel($content, $conf, $currentRecord = '', $dataArray = [])
     {
-        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
-            if (!$currentRecord) {
-                $currentRecord = $this->currentRecord;
+        if (!$this->getTypoScriptFrontendController()->isBackendUserLoggedIn()) {
+            return $content;
+        }
+        if (!$this->getTypoScriptFrontendController()->displayEditIcons) {
+            return $content;
+        }
+
+        if (!$currentRecord) {
+            $currentRecord = $this->currentRecord;
+        }
+        if (empty($dataArray)) {
+            $dataArray = $this->data;
+        }
+
+        if ($conf['newRecordFromTable']) {
+            $currentRecord = $conf['newRecordFromTable'] . ':NEW';
+            $conf['allow'] = 'new';
+            $checkEditAccessInternals = false;
+        } else {
+            $checkEditAccessInternals = true;
+        }
+        list($table, $uid) = explode(':', $currentRecord);
+        // Page ID for new records, 0 if not specified
+        $newRecordPid = (int)$conf['newRecordInPid'];
+        $newUid = null;
+        if (!$conf['onlyCurrentPid'] || $dataArray['pid'] == $this->getTypoScriptFrontendController()->id) {
+            if ($table === 'pages') {
+                $newUid = $uid;
+            } else {
+                if ($conf['newRecordFromTable']) {
+                    $newUid = $this->getTypoScriptFrontendController()->id;
+                    if ($newRecordPid) {
+                        $newUid = $newRecordPid;
+                    }
+                } else {
+                    $newUid = -1 * $uid;
+                }
             }
-            if (empty($dataArr)) {
-                $dataArr = $this->data;
+        }
+        if ($table && $this->getFrontendBackendUser()->allowedToEdit($table, $dataArray, $conf, $checkEditAccessInternals) && $this->getFrontendBackendUser()->allowedToEditLanguage($table, $dataArray)) {
+            $editClass = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/classes/class.frontendedit.php']['edit'];
+            if ($editClass) {
+                $edit = GeneralUtility::makeInstance($editClass);
+                $allowedActions = $this->getFrontendBackendUser()->getAllowedEditActions($table, $conf, $dataArray['pid']);
+                $content = $edit->editPanel($content, $conf, $currentRecord, $dataArray, $table, $allowedActions, $newUid, []);
             }
-            // Delegate rendering of the edit panel to the frontend edit
-            $content = $this->getFrontendBackendUser()->frontendEdit->displayEditPanel($content, $conf, $currentRecord, $dataArr);
         }
         return $content;
     }
@@ -7095,21 +6863,46 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $params The parameters defining which table and fields to edit. Syntax is [tablename]:[fieldname],[fieldname],[fieldname],... OR [fieldname],[fieldname],[fieldname],... (basically "[tablename]:" is optional, default table is the one of the "current record" used in the function). The fieldlist is sent as "&columnsOnly=" parameter to FormEngine
      * @param array $conf TypoScript properties for configuring the edit icons.
      * @param string $currentRecord The "table:uid" of the record being shown. If empty string then $this->currentRecord is used. For new records (set by $conf['newRecordFromTable']) it's auto-generated to "[tablename]:NEW
-     * @param array $dataArr Alternative data array to use. Default is $this->data
+     * @param array $dataArray Alternative data array to use. Default is $this->data
      * @param string $addUrlParamStr Additional URL parameters for the link pointing to FormEngine
      * @return string The input content string, possibly with edit icons added (not necessarily in the end but just after the last string of normal content.
      */
-    public function editIcons($content, $params, array $conf = [], $currentRecord = '', $dataArr = [], $addUrlParamStr = '')
+    public function editIcons($content, $params, array $conf = [], $currentRecord = '', $dataArray = [], $addUrlParamStr = '')
     {
-        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
-            if (!$currentRecord) {
-                $currentRecord = $this->currentRecord;
-            }
-            if (empty($dataArr)) {
-                $dataArr = $this->data;
+        if (!$this->getTypoScriptFrontendController()->isBackendUserLoggedIn()) {
+            return $content;
+        }
+        if (!$this->getTypoScriptFrontendController()->displayFieldEditIcons) {
+            return $content;
+        }
+        if (!$currentRecord) {
+            $currentRecord = $this->currentRecord;
+        }
+        if (empty($dataArray)) {
+            $dataArray = $this->data;
+        }
+        // Check incoming params:
+        list($currentRecordTable, $currentRecordUID) = explode(':', $currentRecord);
+        list($fieldList, $table) = array_reverse(GeneralUtility::trimExplode(':', $params, true));
+        // Reverse the array because table is optional
+        if (!$table) {
+            $table = $currentRecordTable;
+        } elseif ($table != $currentRecordTable) {
+            // If the table is set as the first parameter, and does not match the table of the current record, then just return.
+            return $content;
+        }
+
+        $editUid = $dataArray['_LOCALIZED_UID'] ?: $currentRecordUID;
+        // Edit icons imply that the editing action is generally allowed, assuming page and content element permissions permit it.
+        if (!array_key_exists('allow', $conf)) {
+            $conf['allow'] = 'edit';
+        }
+        if ($table && $this->getFrontendBackendUser()->allowedToEdit($table, $dataArray, $conf, true) && $fieldList && $this->getFrontendBackendUser()->allowedToEditLanguage($table, $dataArray)) {
+            $editClass = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/classes/class.frontendedit.php']['edit'];
+            if ($editClass) {
+                $edit = GeneralUtility::makeInstance($editClass);
+                $content = $edit->editIcons($content, $params, $conf, $currentRecord, $dataArray, $addUrlParamStr, $table, $editUid, $fieldList);
             }
-            // Delegate rendering of the edit panel to frontend edit class.
-            $content = $this->getFrontendBackendUser()->frontendEdit->displayEditIcons($content, $params, $conf, $currentRecord, $dataArr, $addUrlParamStr);
         }
         return $content;
     }
@@ -7120,7 +6913,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
      * @param string $table The table name
      * @param array $row The data record
      * @return bool
-     * @access private
+     * @internal
      * @see editPanelPreviewBorder()
      */
     public function isDisabled($table, $row)
@@ -7168,7 +6961,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
 
         $cacheKey = $this->calculateCacheKey($configuration);
         if (!empty($cacheKey)) {
-            /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
+            /** @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cacheFrontend */
             $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)
                 ->getCache('cache_hash');
             $content = $cacheFrontend->get($cacheKey);