[FEATURE] Add Contexts for storing data access modes
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / ContentObjectRenderer.php
index 88d396b..e80cd19 100644 (file)
@@ -16,8 +16,11 @@ namespace TYPO3\CMS\Frontend\ContentObject;
 
 use Doctrine\DBAL\DBALException;
 use Doctrine\DBAL\Driver\Statement;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
 use TYPO3\CMS\Core\Cache\CacheManager;
-use TYPO3\CMS\Core\Charset\CharsetConverter;
+use TYPO3\CMS\Core\Context\Context;
+use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
@@ -26,9 +29,9 @@ 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;
@@ -38,6 +41,7 @@ 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\MarkerBasedTemplateService;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
@@ -45,10 +49,10 @@ 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\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 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\Extbase\Service\FlexFormService;
@@ -72,8 +76,10 @@ use TYPO3\CMS\Frontend\Typolink\UnableToLinkException;
  * The class is normally instantiated and referred to as "cObj".
  * When you call your own PHP-code typically through a USER or USER_INT cObject then it is this class that instantiates the object and calls the main method. Before it does so it will set (if you are using classes) a reference to itself in the internal variable "cObj" of the object. Thus you can access all functions and data from this class by $this->cObj->... from within you classes written to be USER or USER_INT content objects.
  */
-class ContentObjectRenderer
+class ContentObjectRenderer implements LoggerAwareInterface
 {
+    use LoggerAwareTrait;
+
     /**
      * @var array
      */
@@ -173,8 +179,6 @@ class ContentObjectRenderer
         'bytes.' => 'array',
         'substring' => 'parameters',
         'substring.' => 'array',
-        'removeBadHTML' => 'boolean',
-        'removeBadHTML.' => 'array',
         'cropHTML' => 'crop',
         'cropHTML.' => 'array',
         'stripHtml' => 'boolean',
@@ -200,8 +204,6 @@ class ContentObjectRenderer
         'innerWrap.' => 'array',
         'innerWrap2' => 'wrap',
         'innerWrap2.' => 'array',
-        'fontTag' => 'wrap',
-        'fontTag.' => 'array',
         'addParams.' => 'array',
         'filelink.' => 'array',
         'preCObject' => 'cObject',
@@ -211,13 +213,6 @@ class ContentObjectRenderer
         'wrapAlign' => 'align',
         'wrapAlign.' => 'array',
         'typolink.' => 'array',
-        'TCAselectItem.' => 'array',
-        'space' => 'space',
-        'space.' => 'array',
-        'spaceBefore' => 'int',
-        'spaceBefore.' => 'array',
-        'spaceAfter' => 'int',
-        'spaceAfter.' => 'array',
         'wrap' => 'wrap',
         'wrap.' => 'array',
         'noTrimWrap' => 'wrap',
@@ -410,7 +405,7 @@ class ContentObjectRenderer
     /**
      * @var File Current file objects (during iterations over files)
      */
-    protected $currentFile = null;
+    protected $currentFile;
 
     /**
      * Set to TRUE by doConvertToUserIntObject() if USER object wants to become USER_INT
@@ -555,29 +550,23 @@ class ContentObjectRenderer
         $this->table = $table;
         $this->currentRecord = $table !== '' ? $table . ':' . $this->data['uid'] : '';
         $this->parameters = [];
-        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'])) {
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'] as $classArr) {
-                $this->cObjHookObjectsRegistry[$classArr[0]] = $classArr[1];
-            }
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'] ?? [] as $classArr) {
+            $this->cObjHookObjectsRegistry[$classArr[0]] = $classArr[1];
         }
         $this->stdWrapHookObjects = [];
-        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'])) {
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'] as $classData) {
-                $hookObject = GeneralUtility::getUserObj($classData);
-                if (!$hookObject instanceof ContentObjectStdWrapHookInterface) {
-                    throw new \UnexpectedValueException($classData . ' must implement interface ' . ContentObjectStdWrapHookInterface::class, 1195043965);
-                }
-                $this->stdWrapHookObjects[] = $hookObject;
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'] ?? [] as $className) {
+            $hookObject = GeneralUtility::makeInstance($className);
+            if (!$hookObject instanceof ContentObjectStdWrapHookInterface) {
+                throw new \UnexpectedValueException($className . ' must implement interface ' . ContentObjectStdWrapHookInterface::class, 1195043965);
             }
+            $this->stdWrapHookObjects[] = $hookObject;
         }
-        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'])) {
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'] as $classData) {
-                $postInitializationProcessor = GeneralUtility::getUserObj($classData);
-                if (!$postInitializationProcessor instanceof ContentObjectPostInitHookInterface) {
-                    throw new \UnexpectedValueException($classData . ' must implement interface ' . ContentObjectPostInitHookInterface::class, 1274563549);
-                }
-                $postInitializationProcessor->postProcessContentObjectInitialization($this);
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'] ?? [] as $className) {
+            $postInitializationProcessor = GeneralUtility::makeInstance($className);
+            if (!$postInitializationProcessor instanceof ContentObjectPostInitHookInterface) {
+                throw new \UnexpectedValueException($className . ' must implement interface ' . ContentObjectPostInitHookInterface::class, 1274563549);
             }
+            $postInitializationProcessor->postProcessContentObjectInitialization($this);
         }
     }
 
@@ -601,14 +590,12 @@ class ContentObjectRenderer
     {
         if (!isset($this->getImgResourceHookObjects)) {
             $this->getImgResourceHookObjects = [];
-            if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'])) {
-                foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'] as $classData) {
-                    $hookObject = GeneralUtility::getUserObj($classData);
-                    if (!$hookObject instanceof ContentObjectGetImageResourceHookInterface) {
-                        throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetImageResourceHookInterface::class, 1218636383);
-                    }
-                    $this->getImgResourceHookObjects[] = $hookObject;
+            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'] ?? [] as $className) {
+                $hookObject = GeneralUtility::makeInstance($className);
+                if (!$hookObject instanceof ContentObjectGetImageResourceHookInterface) {
+                    throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetImageResourceHookInterface::class, 1218636383);
                 }
+                $this->getImgResourceHookObjects[] = $hookObject;
             }
         }
         return $this->getImgResourceHookObjects;
@@ -722,7 +709,7 @@ class ContentObjectRenderer
                 // Application defined cObjects
                 if (!empty($this->cObjHookObjectsRegistry[$name])) {
                     if (empty($this->cObjHookObjectsArr[$name])) {
-                        $this->cObjHookObjectsArr[$name] = GeneralUtility::getUserObj($this->cObjHookObjectsRegistry[$name]);
+                        $this->cObjHookObjectsArr[$name] = GeneralUtility::makeInstance($this->cObjHookObjectsRegistry[$name]);
                     }
                     $hookObj = $this->cObjHookObjectsArr[$name];
                     if (method_exists($hookObj, 'cObjGetSingleExt')) {
@@ -736,9 +723,9 @@ class ContentObjectRenderer
                         $content .= $this->render($contentObject, $conf);
                     } else {
                         // Call hook functions for extra processing
-                        if ($name && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'])) {
-                            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'] as $classData) {
-                                $hookObject = GeneralUtility::getUserObj($classData);
+                        if ($name) {
+                            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'] ?? [] as $className) {
+                                $hookObject = GeneralUtility::makeInstance($className);
                                 if (!$hookObject instanceof ContentObjectGetSingleHookInterface) {
                                     throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetSingleHookInterface::class, 1195043731);
                                 }
@@ -768,7 +755,7 @@ class ContentObjectRenderer
      * in $this->contentObjectClassMap
      *
      * @param string $name
-     * @return NULL|AbstractContentObject
+     * @return AbstractContentObject|null
      * @throws ContentRenderingException
      */
     public function getContentObject($name)
@@ -806,7 +793,7 @@ class ContentObjectRenderer
         $content = '';
 
         // Evaluate possible cache and return
-        $cacheConfiguration = isset($configuration['cache.']) ? $configuration['cache.'] : null;
+        $cacheConfiguration = $configuration['cache.'] ?? null;
         if ($cacheConfiguration !== null) {
             unset($configuration['cache.']);
             $cache = $this->getFromCache($cacheConfiguration);
@@ -826,16 +813,15 @@ class ContentObjectRenderer
             $exceptionHandler = $this->createExceptionHandler($configuration);
             if ($exceptionHandler === null) {
                 throw $exception;
-            } else {
-                $content = $exceptionHandler->handle($exception, $contentObject, $configuration);
             }
+            $content = $exceptionHandler->handle($exception, $contentObject, $configuration);
         }
 
         // Store cache
         if ($cacheConfiguration !== null) {
             $key = $this->calculateCacheKey($cacheConfiguration);
             if (!empty($key)) {
-                /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend */
+                /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
                 $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
                 $tags = $this->calculateCacheTags($cacheConfiguration);
                 $lifetime = $this->calculateCacheLifetime($cacheConfiguration);
@@ -851,7 +837,7 @@ class ContentObjectRenderer
      * or, from global configuration if not explicitly disabled in local configuration
      *
      * @param array $configuration
-     * @return NULL|ExceptionHandlerInterface
+     * @return ExceptionHandlerInterface|null
      * @throws ContentRenderingException
      */
     protected function createExceptionHandler($configuration = [])
@@ -872,7 +858,7 @@ class ContentObjectRenderer
      * Determine exception handler class name from global and content object configuration
      *
      * @param array $configuration
-     * @return string|NULL
+     * @return string|null
      */
     protected function determineExceptionHandlerClassName($configuration)
     {
@@ -1054,7 +1040,7 @@ class ContentObjectRenderer
         if (!is_array($info)) {
             return '';
         }
-        if (is_file(PATH_site . $info['3'])) {
+        if (is_file(Environment::getPublicPath() . '/' . $info['3'])) {
             $source = $tsfe->absRefPrefix . str_replace('%2F', '/', rawurlencode($info['3']));
         } else {
             $source = $info[3];
@@ -1080,7 +1066,7 @@ class ContentObjectRenderer
             'altParams' => $altParam,
             'border' =>  $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
             'sourceCollection' => $sourceCollection,
-            'selfClosingTagSlash' => (!empty($tsfe->xhtmlDoctype) ? ' /' : ''),
+            'selfClosingTagSlash' => !empty($tsfe->xhtmlDoctype) ? ' /' : '',
         ];
 
         $theValue = $this->templateService->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
@@ -1177,7 +1163,7 @@ class ContentObjectRenderer
                 ];
 
                 if (isset($sourceConfiguration['quality']) || isset($sourceConfiguration['quality.'])) {
-                    $imageQuality = isset($sourceConfiguration['quality']) ? $sourceConfiguration['quality'] : '';
+                    $imageQuality = $sourceConfiguration['quality'] ?? '';
                     if (isset($sourceConfiguration['quality.'])) {
                         $imageQuality = $this->stdWrap($sourceConfiguration['quality'], $sourceConfiguration['quality.']);
                     }
@@ -1191,7 +1177,7 @@ class ContentObjectRenderer
                 } else {
                     $pixelDensity = 1;
                 }
-                $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH'];
+                $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
                 foreach ($dimensionKeys as $dimensionKey) {
                     $dimension = $this->stdWrap($sourceConfiguration[$dimensionKey], $sourceConfiguration[$dimensionKey . '.']);
                     if (!$dimension) {
@@ -1204,6 +1190,12 @@ class ContentObjectRenderer
                             if ($dimensionParts[1]) {
                                 $dimension .= $dimensionParts[1];
                             }
+                        } elseif ($dimensionKey === 'XY') {
+                            $dimensionParts = GeneralUtility::intExplode(',', $dimension, false, 2);
+                            $dimension = $dimensionParts[0] * $pixelDensity;
+                            if ($dimensionParts[1]) {
+                                $dimension .= ',' . $dimensionParts[1] * $pixelDensity;
+                            }
                         } else {
                             $dimension = (int)$dimension * $pixelDensity;
                         }
@@ -1225,17 +1217,15 @@ class ContentObjectRenderer
 
                     $oneSourceCollection = $this->templateService->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
 
-                    if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'])) {
-                        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] as $classData) {
-                            $hookObject = GeneralUtility::getUserObj($classData);
-                            if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) {
-                                throw new \UnexpectedValueException(
-                                    '$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class,
-                                    1380007853
-                                );
-                            }
-                            $oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this);
+                    foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] ?? [] as $className) {
+                        $hookObject = GeneralUtility::makeInstance($className);
+                        if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) {
+                            throw new \UnexpectedValueException(
+                                '$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class,
+                                1380007853
+                            );
                         }
+                        $oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this);
                     }
 
                     $sourceCollection .= $oneSourceCollection;
@@ -1335,7 +1325,7 @@ class ContentObjectRenderer
                     }
                 }
 
-                $processedFile = $file->process('Image.CropScaleMask', $conf);
+                $processedFile = $file->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $conf);
                 $JSwindowExpand = isset($conf['JSwindow.']['expand.']) ? $this->stdWrap($conf['JSwindow.']['expand'], $conf['JSwindow.']['expand.']) : $conf['JSwindow.']['expand'];
                 $offset = GeneralUtility::intExplode(',', $JSwindowExpand . ',');
                 $newWindow = isset($conf['JSwindow.']['newWindow.']) ? $this->stdWrap($conf['JSwindow.']['newWindow'], $conf['JSwindow.']['newWindow.']) : $conf['JSwindow.']['newWindow'];
@@ -1364,34 +1354,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * Returns content of a file. If it's an image the content of the file is not returned but rather an image tag is.
-     *
-     * @param string $fName The filename, being a TypoScript resource data type
-     * @param string $addParams Additional parameters (attributes). Default is empty alt and title tags.
-     * @return string If jpg,gif,jpeg,png: returns image_tag with picture in. If html,txt: returns content string
-     * @see FILE()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, use file_get_contents() directly
-     */
-    public function fileResource($fName, $addParams = 'alt="" title=""')
-    {
-        GeneralUtility::logDeprecatedFunction();
-        $tsfe = $this->getTypoScriptFrontendController();
-        $incFile = $tsfe->tmpl->getFileName($fName);
-        if ($incFile && file_exists($incFile)) {
-            $fileInfo = GeneralUtility::split_fileref($incFile);
-            $extension = $fileInfo['fileext'];
-            if ($extension === 'jpg' || $extension === 'jpeg' || $extension === 'gif' || $extension === 'png') {
-                $imgFile = $incFile;
-                $imgInfo = @getimagesize($imgFile);
-                return '<img src="' . htmlspecialchars($tsfe->absRefPrefix . $imgFile) . '" width="' . (int)$imgInfo[0] . '" height="' . (int)$imgInfo[1] . '"' . $this->getBorderAttr(' border="0"') . ' ' . $addParams . ' />';
-            } elseif (filesize($incFile) < 1024 * 1024) {
-                return file_get_contents($incFile);
-            }
-        }
-        return '';
-    }
-
-    /**
      * Sets the SYS_LASTCHANGED timestamp if input timestamp is larger than current value.
      * The SYS_LASTCHANGED timestamp can be used by various caching/indexing applications to determine if the page has new content.
      * Therefore you should call this function with the last-changed timestamp of any element you display.
@@ -1486,15 +1448,13 @@ class ContentObjectRenderer
             $aTagParams = ' ' . trim($this->getTypoScriptFrontendController()->ATagParams . $aTagParams);
         }
         // Extend params
-        if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'])) {
-            $_params = [
-                'conf' => &$conf,
-                'aTagParams' => &$aTagParams
-            ];
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'] as $objRef) {
-                $processor =& GeneralUtility::getUserObj($objRef);
-                $aTagParams = $processor->process($_params, $this);
-            }
+        $_params = [
+            'conf' => &$conf,
+            'aTagParams' => &$aTagParams
+        ];
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'] ?? [] as $className) {
+            $processor = & GeneralUtility::makeInstance($className);
+            $aTagParams = $processor->process($_params, $this);
         }
 
         $aTagParams = trim($aTagParams);
@@ -1517,7 +1477,7 @@ class ContentObjectRenderer
     {
         $out = '';
         if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']) {
-            $extLinkATagParamsHandler = GeneralUtility::getUserObj($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));
             }
@@ -1530,204 +1490,6 @@ class ContentObjectRenderer
      * HTML template processing functions
      *
      ***********************************************/
-    /**
-     * Returns a subpart from the input content stream.
-     * A subpart is a part of the input stream which is encapsulated in a
-     * string matching the input string, $marker. If this string is found
-     * inside of HTML comment tags the start/end points of the content block
-     * returned will be that right outside that comment block.
-     * Example: The contennt string is
-     * "Hello <!--###sub1### begin--> World. How are <!--###sub1### end--> you?"
-     * If $marker is "###sub1###" then the content returned is
-     * " World. How are ". The input content string could just as well have
-     * been "Hello ###sub1### World. How are ###sub1### you?" and the result
-     * would be the same
-     * Wrapper for \TYPO3\CMS\Core\Utility\MarkerBasedTemplateService::getSubpart which behaves identical
-     *
-     * @param string $content The content stream, typically HTML template content.
-     * @param string $marker The marker string, typically on the form "###[the marker string]###
-     * @return string The subpart found, if found.
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function getSubpart($content, $marker)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->templateService->getSubpart($content, $marker);
-    }
-
-    /**
-     * Substitute subpart in input template stream.
-     * This function substitutes a subpart in $content with the content of
-     * $subpartContent.
-     * Wrapper for \TYPO3\CMS\Core\Utility\MarkerBasedTemplateService::substituteSubpart which behaves identical
-     *
-     * @param string $content The content stream, typically HTML template content.
-     * @param string $marker The marker string, typically on the form "###[the marker string]###
-     * @param mixed $subpartContent The content to insert instead of the subpart found. If a string, then just plain substitution happens (includes removing the HTML comments of the subpart if found). If $subpartContent happens to be an array, it's [0] and [1] elements are wrapped around the EXISTING content of the subpart (fetched by getSubpart()) thereby not removing the original content.
-     * @param bool|int $recursive If $recursive is set, the function calls itself with the content set to the remaining part of the content after the second marker. This means that proceding subparts are ALSO substituted!
-     * @return string The processed HTML content string.
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function substituteSubpart($content, $marker, $subpartContent, $recursive = 1)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->templateService->substituteSubpart($content, $marker, $subpartContent, $recursive);
-    }
-
-    /**
-     * Substitues multiple subparts at once
-     *
-     * @param string $content The content stream, typically HTML template content.
-     * @param array $subpartsContent The array of key/value pairs being subpart/content values used in the substitution. For each element in this array the function will substitute a subpart in the content stream with the content.
-     * @return string The processed HTML content string.
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function substituteSubpartArray($content, array $subpartsContent)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->templateService->substituteSubpartArray($content, $subpartsContent);
-    }
-
-    /**
-     * Substitutes a marker string in the input content
-     * (by a simple str_replace())
-     *
-     * @param string $content The content stream, typically HTML template content.
-     * @param string $marker The marker string, typically on the form "###[the marker string]###
-     * @param mixed $markContent The content to insert instead of the marker string found.
-     * @return string The processed HTML content string.
-     * @see substituteSubpart()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function substituteMarker($content, $marker, $markContent)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->templateService->substituteMarker($content, $marker, $markContent);
-    }
-
-    /**
-     * Multi substitution function with caching.
-     *
-     * This function should be a one-stop substitution function for working
-     * with HTML-template. It does not substitute by str_replace but by
-     * splitting. This secures that the value inserted does not themselves
-     * contain markers or subparts.
-     *
-     * Note that the "caching" won't cache the content of the substition,
-     * but only the splitting of the template in various parts. So if you
-     * want only one cache-entry per template, make sure you always pass the
-     * exact same set of marker/subpart keys. Else you will be flooding the
-     * user's cache table.
-     *
-     * This function takes three kinds of substitutions in one:
-     * $markContentArray is a regular marker-array where the 'keys' are
-     * substituted in $content with their values
-     *
-     * $subpartContentArray works exactly like markContentArray only is whole
-     * subparts substituted and not only a single marker.
-     *
-     * $wrappedSubpartContentArray is an array of arrays with 0/1 keys where
-     * the subparts pointed to by the main key is wrapped with the 0/1 value
-     * alternating.
-     *
-     * @param string $content The content stream, typically HTML template content.
-     * @param array $markContentArray Regular marker-array where the 'keys' are substituted in $content with their values
-     * @param array $subpartContentArray Exactly like markContentArray only is whole subparts substituted and not only a single marker.
-     * @param array $wrappedSubpartContentArray An array of arrays with 0/1 keys where the subparts pointed to by the main key is wrapped with the 0/1 value alternating.
-     * @return string The output content stream
-     * @see substituteSubpart(), substituteMarker(), substituteMarkerInObject(), TEMPLATE()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function substituteMarkerArrayCached($content, array $markContentArray = null, array $subpartContentArray = null, array $wrappedSubpartContentArray = null)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->templateService->substituteMarkerArrayCached($content, $markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
-    }
-
-    /**
-     * Traverses the input $markContentArray array and for each key the marker
-     * by the same name (possibly wrapped and in upper case) will be
-     * substituted with the keys value in the array.
-     *
-     * This is very useful if you have a data-record to substitute in some
-     * content. In particular when you use the $wrap and $uppercase values to
-     * pre-process the markers. Eg. a key name like "myfield" could effectively
-     * be represented by the marker "###MYFIELD###" if the wrap value
-     * was "###|###" and the $uppercase boolean TRUE.
-     *
-     * @param string $content The content stream, typically HTML template content.
-     * @param array $markContentArray The array of key/value pairs being marker/content values used in the substitution. For each element in this array the function will substitute a marker in the content stream with the content.
-     * @param string $wrap A wrap value - [part 1] | [part 2] - for the markers before substitution
-     * @param bool $uppercase If set, all marker string substitution is done with upper-case markers.
-     * @param bool $deleteUnused If set, all unused marker are deleted.
-     * @return string The processed output stream
-     * @see substituteMarker(), substituteMarkerInObject(), TEMPLATE()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function substituteMarkerArray($content, array $markContentArray, $wrap = '', $uppercase = false, $deleteUnused = false)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->templateService->substituteMarkerArray($content, $markContentArray, $wrap, $uppercase, $deleteUnused);
-    }
-
-    /**
-     * Substitute marker array in an array of values
-     *
-     * @param mixed $tree If string, then it just calls substituteMarkerArray. If array(and even multi-dim) then for each key/value pair the marker array will be substituted (by calling this function recursively)
-     * @param array $markContentArray The array of key/value pairs being marker/content values used in the substitution. For each element in this array the function will substitute a marker in the content string/array values.
-     * @return mixed The processed input variable.
-     * @see substituteMarker()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function substituteMarkerInObject(&$tree, array $markContentArray)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        if (is_array($tree)) {
-            foreach ($tree as $key => $value) {
-                $this->templateService->substituteMarkerInObject($tree[$key], $markContentArray);
-            }
-        } else {
-            $tree = $this->templateService->substituteMarkerArray($tree, $markContentArray);
-        }
-        return $tree;
-    }
-
-    /**
-     * Replaces all markers and subparts in a template with the content provided in the structured array.
-     *
-     * @param string $content
-     * @param array $markersAndSubparts
-     * @param string $wrap
-     * @param bool $uppercase
-     * @param bool $deleteUnused
-     * @return string
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = false, $deleteUnused = false)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->templateService->substituteMarkerAndSubpartArrayRecursive($content, $markersAndSubparts, $wrap, $uppercase, $deleteUnused);
-    }
-
-    /**
-     * Adds elements to the input $markContentArray based on the values from
-     * the fields from $fieldList found in $row
-     *
-     * @param array $markContentArray Array with key/values being marker-strings/substitution values.
-     * @param array $row An array with keys found in the $fieldList (typically a record) which values should be moved to the $markContentArray
-     * @param string $fieldList A list of fields from the $row array to add to the $markContentArray array. If empty all fields from $row will be added (unless they are integers)
-     * @param bool $nl2br If set, all values added to $markContentArray will be nl2br()'ed
-     * @param string $prefix Prefix string to the fieldname before it is added as a key in the $markContentArray. Notice that the keys added to the $markContentArray always start and end with "###
-     * @param bool $HSC If set, all values are passed through htmlspecialchars() - RECOMMENDED to avoid most obvious XSS and maintain XHTML compliance.
-     * @return array The modified $markContentArray
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
-     */
-    public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = true, $prefix = 'FIELD_', $HSC = false)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        $tsfe = $this->getTypoScriptFrontendController();
-        return $this->templateService->fillInMarkerArray($markContentArray, $row, $fieldList, $nl2br, $prefix, $HSC, !empty($tsfe->xhtmlDoctype));
-    }
 
     /**
      * Sets the current file object during iterations over files.
@@ -1821,10 +1583,6 @@ class ContentObjectRenderer
                         $functionName => $conf[$functionName],
                         $functionProperties => $conf[$functionProperties]
                     ];
-                    // In this special case 'spaceBefore' and 'spaceAfter' need additional stuff from 'space.''
-                    if ($functionName === 'spaceBefore' || $functionName === 'spaceAfter') {
-                        $singleConf['space.'] = $conf['space.'];
-                    }
                     // Hand over the whole $conf array to the stdWrapHookObjects
                     if ($functionType === 'hook') {
                         $singleConf = $conf;
@@ -2116,13 +1874,13 @@ class ContentObjectRenderer
      * ifNull
      * Will set content to a replacement value in case the value of content is NULL
      *
-     * @param string|NULL $content Input value undergoing processing in this function.
+     * @param string|null $content Input value undergoing processing in this function.
      * @param array $conf stdWrap properties for ifNull.
      * @return string The processed input value
      */
     public function stdWrap_ifNull($content = '', $conf = [])
     {
-        return $content !== null ? $content : $conf['ifNull'];
+        return $content ?? $conf['ifNull'];
     }
 
     /**
@@ -2320,12 +2078,10 @@ class ContentObjectRenderer
     public function stdWrap_csConv($content = '', $conf = [])
     {
         if (!empty($conf['csConv'])) {
-            $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
-            $output = $charsetConverter->conv($content, $charsetConverter->parse_charset($conf['csConv']), 'utf-8');
+            $output = mb_convert_encoding($content, 'utf-8', trim(strtolower($conf['csConv'])));
             return $output !== false && $output !== '' ? $output : $content;
-        } else {
-            return $content;
         }
+        return $content;
     }
 
     /**
@@ -2520,9 +2276,7 @@ class ContentObjectRenderer
         $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
         $content = $conf['strftime.']['GMT'] ? gmstrftime($conf['strftime'], $content) : strftime($conf['strftime'], $content);
         if (!empty($conf['strftime.']['charset'])) {
-            /** @var CharsetConverter $charsetConverter */
-            $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
-            $output = $charsetConverter->conv($content, $charsetConverter->parse_charset($conf['strftime.']['charset']), 'utf-8');
+            $output = mb_convert_encoding($content, 'utf-8', trim(strtolower($conf['strftime.']['charset'])));
             return $output ?: $content;
         }
         return $content;
@@ -2598,19 +2352,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * removeBadHTML
-     * Removes HTML tags based on stdWrap properties
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @return string The processed input value
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
-     */
-    public function stdWrap_removeBadHTML($content = '')
-    {
-        return $this->removeBadHTML($content);
-    }
-
-    /**
      * cropHTML
      * Crops content to a given size while leaving HTML tags untouched
      *
@@ -2786,22 +2527,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * fontTag
-     * A wrap formerly used to apply font tags to format the content
-     * See wrap
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for fontTag.
-     * @return string The processed input value
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
-     */
-    public function stdWrap_fontTag($content = '', $conf = [])
-    {
-        GeneralUtility::logDeprecatedFunction();
-        return $this->wrap($content, $conf['fontTag']);
-    }
-
-    /**
      * addParams
      * Adds tag attributes to any content that is a tag
      *
@@ -2888,65 +2613,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * TCAselectItem
-     * Returns a list of options available for a given field in the DB which has to be of the type select
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for TCAselectItem.
-     * @return string The processed input value
-     */
-    public function stdWrap_TCAselectItem($content = '', $conf = [])
-    {
-        if (is_array($conf['TCAselectItem.'])) {
-            $content = $this->TCAlookup($content, $conf['TCAselectItem.']);
-        }
-        return $content;
-    }
-
-    /**
-     * spaceBefore
-     * Will add space before the current content
-     * By default this is done with a clear.gif but it can be done with CSS margins by setting the property space.useDiv to TRUE
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for spaceBefore and space.
-     * @return string The processed input value
-     */
-    public function stdWrap_spaceBefore($content = '', $conf = [])
-    {
-        return $this->wrapSpace($content, trim($conf['spaceBefore']) . '|', $conf['space.']);
-    }
-
-    /**
-     * spaceAfter
-     * Will add space after the current content
-     * By default this is done with a clear.gif but it can be done with CSS margins by setting the property space.useDiv to TRUE
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for spaceAfter and space.
-     * @return string The processed input value
-     */
-    public function stdWrap_spaceAfter($content = '', $conf = [])
-    {
-        return $this->wrapSpace($content, '|' . trim($conf['spaceAfter']), $conf['space.']);
-    }
-
-    /**
-     * space
-     * Will add space before or after the current content
-     * By default this is done with a clear.gif but it can be done with CSS margins by setting the property space.useDiv to TRUE
-     * See wrap
-     *
-     * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for space.
-     * @return string The processed input value
-     */
-    public function stdWrap_space($content = '', $conf = [])
-    {
-        return $this->wrapSpace($content, trim($conf['space']), $conf['space.']);
-    }
-
-    /**
      * wrap
      * This is the "mother" of all wraps
      * Third of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
@@ -3160,7 +2826,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_editIcons($content = '', $conf = [])
     {
-        if ($this->getTypoScriptFrontendController()->beUserLogin && $conf['editIcons']) {
+        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $conf['editIcons']) {
             if (!is_array($conf['editIcons.'])) {
                 $conf['editIcons.'] = [];
             }
@@ -3179,7 +2845,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_editPanel($content = '', $conf = [])
     {
-        if ($this->getTypoScriptFrontendController()->beUserLogin) {
+        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn()) {
             $content = $this->editPanel($content, $conf['editPanel.']);
         }
         return $content;
@@ -3201,20 +2867,18 @@ class ContentObjectRenderer
         if (empty($key)) {
             return $content;
         }
-        /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend */
+        /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
         $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
         $tags = $this->calculateCacheTags($conf['cache.']);
         $lifetime = $this->calculateCacheLifetime($conf['cache.']);
-        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'])) {
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'] as $_funcRef) {
-                $params = [
-                    'key' => $key,
-                    'content' => $content,
-                    'lifetime' => $lifetime,
-                    'tags' => $tags
-                ];
-                GeneralUtility::callUserFunction($_funcRef, $params, $this);
-            }
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'] ?? [] as $_funcRef) {
+            $params = [
+                'key' => $key,
+                'content' => $content,
+                'lifetime' => $lifetime,
+                'tags' => $tags
+            ];
+            GeneralUtility::callUserFunction($_funcRef, $params, $this);
         }
         $cacheFrontend->set($key, $content, $tags, $lifetime);
         return $content;
@@ -3410,15 +3074,28 @@ class ContentObjectRenderer
         if ($data === '') {
             return '';
         }
-        $data_arr = explode('|', $data);
+        list($possiblePath, $ext_list, $sorting, $reverse, $useFullPath) = GeneralUtility::trimExplode('|', $data);
         // read directory:
         // MUST exist!
         $path = '';
-        if ($this->getTypoScriptFrontendController()->lockFilePath) {
-            // Cleaning name..., only relative paths accepted.
-            $path = $this->clean_directory($data_arr[0]);
-            // See if path starts with lockFilePath, the additional '/' is needed because clean_directory gets rid of it
-            $path = GeneralUtility::isFirstPartOfStr($path . '/', $this->getTypoScriptFrontendController()->lockFilePath) ? $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 '';
@@ -3427,8 +3104,7 @@ class ContentObjectRenderer
             'files' => [],
             'sorting' => []
         ];
-        $ext_list = strtolower(GeneralUtility::uniqueList($data_arr[1]));
-        $sorting = trim($data_arr[2]);
+        $ext_list = strtolower(GeneralUtility::uniqueList($ext_list));
         // Read dir:
         $d = @dir($path);
         if (is_object($d)) {
@@ -3469,7 +3145,7 @@ class ContentObjectRenderer
         }
         // Sort if required
         if (!empty($items['sorting'])) {
-            if (strtolower(trim($data_arr[3])) !== 'r') {
+            if (strtolower($reverse) !== 'r') {
                 asort($items['sorting']);
             } else {
                 arsort($items['sorting']);
@@ -3478,10 +3154,9 @@ class ContentObjectRenderer
         if (!empty($items['files'])) {
             // Make list
             reset($items['sorting']);
-            $fullPath = trim($data_arr[4]);
             $list_arr = [];
             foreach ($items['sorting'] as $key => $v) {
-                $list_arr[] = $fullPath ? $path . '/' . $items['files'][$key] : $items['files'][$key];
+                $list_arr[] = $useFullPath ? $path . '/' . $items['files'][$key] : $items['files'][$key];
             }
             return implode(',', $list_arr);
         }
@@ -3489,27 +3164,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * Cleans $theDir for slashes in the end of the string and returns the new path, if it exists on the server.
-     *
-     * @param string $theDir Absolute path to directory
-     * @return string The directory path if it existed as was valid to access.
-     * @access private
-     * @see filelist()
-     */
-    public function clean_directory($theDir)
-    {
-        // proceeds if no '//', '..' or '\' is in the $theFile
-        if (GeneralUtility::validPathStr($theDir)) {
-            // Removes all dots, slashes and spaces after a path...
-            $theDir = preg_replace('/[\\/\\. ]*$/', '', $theDir);
-            if (!GeneralUtility::isAbsPath($theDir) && @is_dir($theDir)) {
-                return $theDir;
-            }
-        }
-        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.
@@ -3540,8 +3194,13 @@ class ContentObjectRenderer
     }
 
     /**
-     * Implements the "insertData" property of stdWrap meaning that if strings matching {...} is found in the input string they will be substituted with the return value from getData (datatype) which is passed the content of the curly braces.
-     * Example: If input string is "This is the page title: {page:title}" then the part, '{page:title}', will be substituted with the current pages title field value.
+     * Implements the "insertData" property of stdWrap meaning that if strings matching {...} is found in the input string they
+     * will be substituted with the return value from getData (datatype) which is passed the content of the curly braces.
+     * If the content inside the curly braces starts with a hash sign {#...} it is a field name that must be quoted by Doctrine
+     * DBAL and is skipped here for later processing.
+     *
+     * Example: If input string is "This is the page title: {page:title}" then the part, '{page:title}', will be substituted with
+     * the current pages title field value.
      *
      * @param string $str Input value
      * @return string Processed input value
@@ -3558,6 +3217,12 @@ class ContentObjectRenderer
                 $len = strcspn(substr($str, $pointer), '{');
                 $newVal .= substr($str, $pointer, $len);
                 $inside = true;
+                if (substr($str, $pointer + $len + 1, 1) === '#') {
+                    $len2 = strcspn(substr($str, $pointer + $len), '}');
+                    $newVal .= substr($str, $pointer + $len, $len2);
+                    $len += $len2;
+                    $inside = false;
+                }
             } else {
                 $len = strcspn(substr($str, $pointer), '}') + 1;
                 $newVal .= $this->getData(substr($str, $pointer + 1, $len - 2), $this->data);
@@ -3588,9 +3253,9 @@ class ContentObjectRenderer
         $comment = htmlspecialchars($this->insertData($parts[1]));
         $output = LF
             . str_pad('', $indent, TAB) . '<!-- ' . $comment . ' [begin] -->' . LF
-            . str_pad('', ($indent + 1), TAB) . $content . LF
+            . str_pad('', $indent + 1, TAB) . $content . LF
             . str_pad('', $indent, TAB) . '<!-- ' . $comment . ' [end] -->' . LF
-            . str_pad('', ($indent + 1), TAB);
+            . str_pad('', $indent + 1, TAB);
         return $output;
     }
 
@@ -3608,9 +3273,8 @@ class ContentObjectRenderer
         $options = GeneralUtility::intExplode(',', $options . ',');
         if ($options[1]) {
             return mb_substr($content, $options[0], $options[1], 'utf-8');
-        } else {
-            return mb_substr($content, $options[0], null, 'utf-8');
         }
+        return mb_substr($content, $options[0], null, 'utf-8');
     }
 
     /**
@@ -3748,9 +3412,8 @@ class ContentObjectRenderer
                     }
                     $splittedContent[$offset] = $tempContent;
                     break;
-                } else {
-                    $strLen += $thisStrLen;
                 }
+                $strLen += $thisStrLen;
             }
         }
         // Close cropped tags.
@@ -3764,13 +3427,13 @@ class ContentObjectRenderer
                     continue;
                 }
                 preg_match($chars < 0 ? $closingTagRegEx : $openingTagRegEx, $splittedContent[$offset], $matches);
-                $tagName = isset($matches[1]) ? $matches[1] : null;
+                $tagName = $matches[1] ?? null;
                 if ($tagName !== null) {
                     // Seek for the closing (or opening) tag.
                     $countSplittedContent = count($splittedContent);
                     for ($seekingOffset = $offset + 2; $seekingOffset < $countSplittedContent; $seekingOffset = $seekingOffset + 2) {
                         preg_match($chars < 0 ? $openingTagRegEx : $closingTagRegEx, $splittedContent[$seekingOffset], $matches);
-                        $seekingTagName = isset($matches[1]) ? $matches[1] : null;
+                        $seekingTagName = $matches[1] ?? null;
                         if ($tagName === $seekingTagName) {
                             // We found a matching tag.
                             // Add closing tag only if it occurs after the cropped content item.
@@ -3796,47 +3459,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * Function for removing malicious HTML code when you want to provide some HTML code user-editable.
-     * The purpose is to avoid XSS attacks and the code will be continuously modified to remove such code.
-     * For a complete reference with javascript-on-events, see http://www.wdvl.com/Authoring/JavaScript/Events/events_target.html
-     *
-     * @param string $text Input string to be cleaned.
-     * @return string Return string
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
-     */
-    public function removeBadHTML($text)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        // Copyright 2002-2003 Thomas Bley
-        $text = preg_replace([
-            '\'<script[^>]*?>.*?</script[^>]*?>\'si',
-            '\'<applet[^>]*?>.*?</applet[^>]*?>\'si',
-            '\'<object[^>]*?>.*?</object[^>]*?>\'si',
-            '\'<iframe[^>]*?>.*?</iframe[^>]*?>\'si',
-            '\'<frameset[^>]*?>.*?</frameset[^>]*?>\'si',
-            '\'<style[^>]*?>.*?</style[^>]*?>\'si',
-            '\'<marquee[^>]*?>.*?</marquee[^>]*?>\'si',
-            '\'<script[^>]*?>\'si',
-            '\'<meta[^>]*?>\'si',
-            '\'<base[^>]*?>\'si',
-            '\'<applet[^>]*?>\'si',
-            '\'<object[^>]*?>\'si',
-            '\'<link[^>]*?>\'si',
-            '\'<iframe[^>]*?>\'si',
-            '\'<frame[^>]*?>\'si',
-            '\'<frameset[^>]*?>\'si',
-            '\'<input[^>]*?>\'si',
-            '\'<form[^>]*?>\'si',
-            '\'<embed[^>]*?>\'si',
-            '\'background-image:url\'si',
-            '\'<\\w+.*?(onabort|onbeforeunload|onblur|onchange|onclick|ondblclick|ondragdrop|onerror|onfilterchange|onfocus|onhelp|onkeydown|onkeypress|onkeyup|onload|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onmove|onreadystatechange|onreset|onresize|onscroll|onselect|onselectstart|onsubmit|onunload).*?>\'si'
-        ], '', $text);
-        $text = preg_replace('/<a[^>]*href[[:space:]]*=[[:space:]]*["\']?[[:space:]]*javascript[^>]*/i', '', $text);
-        // Return clean content
-        return $text;
-    }
-
-    /**
      * Implements the TypoScript function "addParams"
      *
      * @param string $content The string with the HTML tag.
@@ -3931,16 +3553,17 @@ class ContentObjectRenderer
             $conf['icon.']['path'] = isset($conf['icon.']['path.'])
                 ? $this->stdWrap($conf['icon.']['path'], $conf['icon.']['path.'])
                 : $conf['icon.']['path'];
-            $iconP = !empty($conf['icon.']['path'])
+            $iconPath = !empty($conf['icon.']['path'])
                 ? $conf['icon.']['path']
-                : ExtensionManagementUtility::siteRelPath('frontend') . 'Resources/Public/Icons/FileIcons/';
+                : 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(($iconP . $fI['fileext'] . $iconExt))
-                ? $iconP . $fI['fileext'] . $iconExt
-                : $iconP . 'default' . $iconExt;
+            $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));
@@ -3948,7 +3571,8 @@ class ContentObjectRenderer
                 if ($conf['iconCObject']) {
                     $icon = $this->cObjGetSingle($conf['iconCObject'], $conf['iconCObject.'], 'iconCObject');
                 } else {
-                    $notFoundThumb = ExtensionManagementUtility::siteRelPath('core') . 'Resources/Public/Images/NotFound.gif';
+                    $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
@@ -4117,7 +3741,7 @@ class ContentObjectRenderer
         // return value directly by returnKey. No further processing
         if (!empty($valArr) && (MathUtility::canBeInterpretedAsInteger($conf['returnKey']) || $conf['returnKey.'])) {
             $key = isset($conf['returnKey.']) ? (int)$this->stdWrap($conf['returnKey'], $conf['returnKey.']) : (int)$conf['returnKey'];
-            return isset($valArr[$key]) ? $valArr[$key] : '';
+            return $valArr[$key] ?? '';
         }
 
         // return the amount of elements. No further processing
@@ -4216,7 +3840,7 @@ class ContentObjectRenderer
                     $modifiers = substr($search, $startModifiers + 1);
                     // remove "e" (eval-modifier), which would otherwise allow to run arbitrary PHP-code
                     $modifiers = str_replace('e', '', $modifiers);
-                    $search = substr($search, 0, ($startModifiers + 1)) . $modifiers;
+                    $search = substr($search, 0, $startModifiers + 1) . $modifiers;
                 }
                 if (empty($useOptionSplitReplace)) {
                     $content = preg_replace($search, $replace, $content);
@@ -4741,9 +4365,10 @@ class ContentObjectRenderer
                 if (!$attrib['align'] && $defaultAlign) {
                     $attrib['align'] = $defaultAlign;
                 }
-                $params = GeneralUtility::implodeAttributes($attrib, 1);
-                if (!($conf['removeWrapping'] && !($emptyTag && $conf['removeWrapping.']['keepSingleTag']))) {
-                    if ($emptyTag) {
+                $params = GeneralUtility::implodeAttributes($attrib, true);
+                if (!$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) : '') . ' />';
                     } else {
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . '>' . $str_content . '</' . strtolower($uTagName) . '>';
@@ -4772,7 +4397,7 @@ class ContentObjectRenderer
     {
         $aTagParams = $this->getATagParams($conf);
         $textstr = '';
-        foreach ([ 'http://', 'https://' ] as $scheme) {
+        foreach (['http://', 'https://'] as $scheme) {
             $textpieces = explode($scheme, $data);
             $pieces = count($textpieces);
             $textstr = $textpieces[0];
@@ -4817,7 +4442,7 @@ class ContentObjectRenderer
 
                     $res = '<a href="' . htmlspecialchars($linkUrl) . '"'
                         . ($target !== '' ? ' target="' . htmlspecialchars($target) . '"' : '')
-                        . $aTagParams . $this->extLinkATagParams(('http://' . $parts[0]), 'url') . '>';
+                        . $aTagParams . $this->extLinkATagParams('http://' . $parts[0], 'url') . '>';
 
                     $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
                     if ((string)$conf['ATagBeforeWrap'] !== '') {
@@ -4900,7 +4525,7 @@ class ContentObjectRenderer
      *
      * @param string|File|FileReference $file A "imgResource" TypoScript data type. Either a TypoScript file resource, a file or a file reference object or the string GIFBUILDER. See description above.
      * @param array $fileArray TypoScript properties for the imgResource type
-     * @return array|NULL Returns info-array
+     * @return array|null Returns info-array
      * @see IMG_RESOURCE(), cImage(), \TYPO3\CMS\Frontend\Imaging\GifBuilder
      */
     public function getImgResource($file, $fileArray)
@@ -4929,9 +4554,6 @@ class ContentObjectRenderer
                 $fileObject = $file;
             } elseif ($file instanceof FileReference) {
                 $fileObject = $file->getOriginalFile();
-                if (!isset($fileArray['crop'])) {
-                    $fileArray['crop'] = $this->getCropArea($file, $fileArray['cropVariant'] ?: 'default');
-                }
             } else {
                 try {
                     if ($fileArray['import.']) {
@@ -4944,11 +4566,8 @@ class ContentObjectRenderer
                     if (MathUtility::canBeInterpretedAsInteger($file)) {
                         $treatIdAsReference = isset($fileArray['treatIdAsReference.']) ? $this->stdWrap($fileArray['treatIdAsReference'], $fileArray['treatIdAsReference.']) : $fileArray['treatIdAsReference'];
                         if (!empty($treatIdAsReference)) {
-                            $fileReference = $this->getResourceFactory()->getFileReferenceObject($file);
-                            $fileObject = $fileReference->getOriginalFile();
-                            if (!isset($fileArray['crop'])) {
-                                $fileArray['crop'] = $this->getCropArea($fileReference, $fileArray['cropVariant'] ?: 'default');
-                            }
+                            $file = $this->getResourceFactory()->getFileReferenceObject($file);
+                            $fileObject = $file->getOriginalFile();
                         } else {
                             $fileObject = $this->getResourceFactory()->getFileObject($file);
                         }
@@ -4961,9 +4580,7 @@ class ContentObjectRenderer
                         $fileObject = $this->getResourceFactory()->retrieveFileOrFolderObject($file);
                     }
                 } catch (Exception $exception) {
-                    /** @var \TYPO3\CMS\Core\Log\Logger $logger */
-                    $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
-                    $logger->warning('The image "' . $file . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]);
+                    $this->logger->warning('The image "' . $file . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]);
                     return null;
                 }
             }
@@ -4979,9 +4596,12 @@ class ContentObjectRenderer
                 $processingConfiguration['noScale'] = isset($fileArray['noScale.']) ? $this->stdWrap($fileArray['noScale'], $fileArray['noScale.']) : $fileArray['noScale'];
                 $processingConfiguration['additionalParameters'] = isset($fileArray['params.']) ? $this->stdWrap($fileArray['params'], $fileArray['params.']) : $fileArray['params'];
                 $processingConfiguration['frame'] = isset($fileArray['frame.']) ? (int)$this->stdWrap($fileArray['frame'], $fileArray['frame.']) : (int)$fileArray['frame'];
-                $processingConfiguration['crop'] = isset($fileArray['crop.'])
-                    ? $this->stdWrap($fileArray['crop'], $fileArray['crop.'])
-                    : (isset($fileArray['crop']) ? $fileArray['crop'] : null);
+                if ($file instanceof FileReference) {
+                    $processingConfiguration['crop'] = $this->getCropAreaFromFileReference($file, $fileArray);
+                } else {
+                    $processingConfiguration['crop'] = $this->getCropAreaFromFromTypoScriptSettings($fileObject, $fileArray);
+                }
+
                 // Possibility to cancel/force profile extraction
                 // see $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_stripColorProfileCommand']
                 if (isset($fileArray['stripProfile'])) {
@@ -5004,10 +4624,8 @@ class ContentObjectRenderer
                         $processingConfiguration['maskImages']['maskBottomImageMask'] = $bottomImg_mask['processedFile'];
                     }
                     $processedFileObject = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
-                    $hash = $processedFileObject->calculateChecksum();
-                    // store info in the TSFE template cache (kept for backwards compatibility)
-                    if ($processedFileObject->isProcessed() && !isset($tsfe->tmpl->fileCache[$hash])) {
-                        $tsfe->tmpl->fileCache[$hash] = [
+                    if ($processedFileObject->isProcessed()) {
+                        $imageResource = [
                             0 => $processedFileObject->getProperty('width'),
                             1 => $processedFileObject->getProperty('height'),
                             2 => $processedFileObject->getExtension(),
@@ -5017,11 +4635,9 @@ class ContentObjectRenderer
                             // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder,
                             // in order for the setup-array to create a unique filename hash.
                             'originalFile' => $fileObject,
-                            'processedFile' => $processedFileObject,
-                            'fileCacheHash' => $hash
+                            'processedFile' => $processedFileObject
                         ];
                     }
-                    $imageResource = $tsfe->tmpl->fileCache[$hash];
                 }
             }
         }
@@ -5051,17 +4667,89 @@ class ContentObjectRenderer
     }
 
     /**
+     * Returns an ImageManipulation\Area object for the given cropVariant (or 'default')
+     * or null if the crop settings or crop area is empty.
+     *
+     * The cropArea from file reference is used, if not set in TypoScript.
+     *
+     * Example TypoScript settings:
+     * file.crop =
+     * OR
+     * file.crop = 50,50,100,100
+     * OR
+     * file.crop.data = file:current:crop
+     *
      * @param FileReference $fileReference
+     * @param array $fileArray TypoScript properties for the imgResource type
+     * @return Area|null
+     */
+    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.
+            $cropVariant = $fileArray['cropVariant'] ?? 'default';
+            $fileCropArea = $this->createCropAreaFromJsonString((string)$fileReference->getProperty('crop'), $cropVariant);
+            return $fileCropArea->isEmpty() ? null : $fileCropArea->makeAbsoluteBasedOnFile($fileReference);
+        }
+
+        return $this->getCropAreaFromFromTypoScriptSettings($fileReference, $fileArray);
+    }
+
+    /**
+     * Returns an ImageManipulation\Area object for the given cropVariant (or 'default')
+     * or null if the crop settings or crop area is empty.
+     *
+     * @param FileInterface $file
+     * @param array $fileArray
+     * @return Area|null
+     */
+    protected function getCropAreaFromFromTypoScriptSettings(FileInterface $file, array $fileArray)
+    {
+        /** @var Area $cropArea */
+        $cropArea = null;
+        // Resolve TypoScript configured cropping.
+        $cropSettings = isset($fileArray['crop.'])
+            ? $this->stdWrap($fileArray['crop'], $fileArray['crop.'])
+            : ($fileArray['crop'] ?? null);
+
+        if (is_string($cropSettings)) {
+            // Set crop variant from TypoScript settings. If not set, use default.
+            $cropVariant = $fileArray['cropVariant'] ?? 'default';
+            // Get cropArea from CropVariantCollection, if cropSettings is a valid json.
+            // CropVariantCollection::create does json_decode.
+            $jsonCropArea = $this->createCropAreaFromJsonString($cropSettings, $cropVariant);
+            $cropArea = $jsonCropArea->isEmpty() ? null : $jsonCropArea->makeAbsoluteBasedOnFile($file);
+
+            // Cropping is configured in TypoScript in the following way: file.crop = 50,50,100,100
+            if ($jsonCropArea->isEmpty() && preg_match('/^[0-9]+,[0-9]+,[0-9]+,[0-9]+$/', $cropSettings)) {
+                $cropSettings = explode(',', $cropSettings);
+                if (count($cropSettings) === 4) {
+                    $stringCropArea = GeneralUtility::makeInstance(
+                        Area::class,
+                        ...$cropSettings
+                    );
+                    $cropArea = $stringCropArea->isEmpty() ? null : $stringCropArea;
+                }
+            }
+        }
+
+        return $cropArea;
+    }
+
+    /**
+     * Takes a JSON string and creates CropVariantCollection and fetches the corresponding
+     * CropArea for that.
+     *
+     * @param string $cropSettings
      * @param string $cropVariant
-     * @return null|\TYPO3\CMS\Core\Imaging\ImageManipulation\Area
+     * @return Area
      */
-    protected function getCropArea(FileReference $fileReference, string $cropVariant)
+    protected function createCropAreaFromJsonString(string $cropSettings, string $cropVariant): Area
     {
-        $cropVariantCollection = CropVariantCollection::create(
-            (string)$fileReference->getProperty('crop')
-        );
-        $cropArea = $cropVariantCollection->getCropArea($cropVariant);
-        return $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($fileReference);
+        return CropVariantCollection::create($cropSettings)->getCropArea($cropVariant);
     }
 
     /***********************************************
@@ -5079,14 +4767,14 @@ class ContentObjectRenderer
     {
         if (!strstr($field, '//')) {
             return $this->data[trim($field)];
-        } else {
-            $sections = GeneralUtility::trimExplode('//', $field, true);
-            foreach ($sections as $k) {
-                if ((string)$this->data[$k] !== '') {
-                    return $this->data[$k];
-                }
+        }
+        $sections = GeneralUtility::trimExplode('//', $field, true);
+        foreach ($sections as $k) {
+            if ((string)$this->data[$k] !== '') {
+                return $this->data[$k];
             }
         }
+
         return '';
     }
 
@@ -5094,7 +4782,7 @@ class ContentObjectRenderer
      * Implements the TypoScript data type "getText". This takes a string with parameters and based on those a value from somewhere in the system is returned.
      *
      * @param string $string The parameter string, eg. "field : title" or "field : navtitle // field : title" (in the latter case and example of how the value is FIRST splitted by "//" is shown)
-     * @param NULL|array $fieldArray Alternative field array; If you set this to an array this variable will be used to look up values for the "field" key. Otherwise the current page record in $GLOBALS['TSFE']->page is used.
+     * @param array|null $fieldArray Alternative field array; If you set this to an array this variable will be used to look up values for the "field" key. Otherwise the current page record in $GLOBALS['TSFE']->page is used.
      * @return string The value fetched
      * @see getFieldVal()
      */
@@ -5203,12 +4891,13 @@ class ContentObjectRenderer
                             $rootLine = $tsfe->rootLine;
                             array_shift($rootLine);
                             foreach ($rootLine as $rootLinePage) {
-                                $retVal = (string) $rootLinePage['backend_layout_next_level'];
+                                $retVal = (string)$rootLinePage['backend_layout_next_level'];
                                 // If layout for "next level" is set to "none" - don't use any and stop searching
                                 if ($retVal === '-1') {
                                     $retVal = 'none';
                                     break;
-                                } elseif ($retVal !== '' && $retVal !== '0') {
+                                }
+                                if ($retVal !== '' && $retVal !== '0') {
                                     // Stop searching if a layout for "next level" is set
                                     break;
                                 }
@@ -5292,14 +4981,13 @@ class ContentObjectRenderer
                         break;
                 }
             }
-            if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'])) {
-                foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'] as $classData) {
-                    $hookObject = GeneralUtility::getUserObj($classData);
-                    if (!$hookObject instanceof ContentObjectGetDataHookInterface) {
-                        throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetDataHookInterface::class, 1195044480);
-                    }
-                    $retVal = $hookObject->getDataExtension($string, $fieldArray, $secVal, $retVal, $this);
+
+            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'] ?? [] as $className) {
+                $hookObject = GeneralUtility::makeInstance($className);
+                if (!$hookObject instanceof ContentObjectGetDataHookInterface) {
+                    throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetDataHookInterface::class, 1195044480);
                 }
+                $retVal = $hookObject->getDataExtension($string, $fieldArray, $secVal, $retVal, $this);
             }
         }
         return $retVal;
@@ -5328,9 +5016,7 @@ class ContentObjectRenderer
                 $fileObject = null;
             }
         } catch (Exception $exception) {
-            /** @var \TYPO3\CMS\Core\Log\Logger $logger */
-            $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
-            $logger->warning('The file "' . $fileUidOrCurrentKeyword . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]);
+            $this->logger->warning('The file "' . $fileUidOrCurrentKeyword . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]);
             $fileObject = null;
         }
 
@@ -5387,14 +5073,14 @@ class ContentObjectRenderer
         $rootLine = is_array($altRootLine) ? $altRootLine : $this->getTypoScriptFrontendController()->tmpl->rootLine;
         if (!$slideBack) {
             return $rootLine[$key][$field];
-        } else {
-            for ($a = $key; $a >= 0; $a--) {
-                $val = $rootLine[$a][$field];
-                if ($val) {
-                    return $val;
-                }
+        }
+        for ($a = $key; $a >= 0; $a--) {
+            $val = $rootLine[$a][$field];
+            if ($val) {
+                return $val;
             }
         }
+
         return '';
     }
 
@@ -5454,40 +5140,6 @@ class ContentObjectRenderer
         return $key;
     }
 
-    /**
-     * Looks up the incoming value in the defined TCA configuration
-     * Works only with TCA-type 'select' and options defined in 'items'
-     *
-     * @param mixed $inputValue Comma-separated list of values to look up
-     * @param array $conf TS-configuration array, see TSref for details
-     * @return string String of translated values, separated by $delimiter. If no matches were found, the input value is simply returned.
-     * @todo It would be nice it this function basically looked up any type of value, db-relations etc.
-     */
-    public function TCAlookup($inputValue, $conf)
-    {
-        $table = $conf['table'];
-        $field = $conf['field'];
-        $delimiter = $conf['delimiter'] ? $conf['delimiter'] : ' ,';
-        if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field]) && is_array($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'])) {
-            $tsfe = $this->getTypoScriptFrontendController();
-            $values = GeneralUtility::trimExplode(',', $inputValue);
-            $output = [];
-            foreach ($values as $value) {
-                // Traverse the items-array...
-                foreach ($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'] as $item) {
-                    // ... and return the first found label where the value was equal to $key
-                    if ((string)$item[1] === trim($value)) {
-                        $output[] = $tsfe->sL($item[0]);
-                    }
-                }
-            }
-            $returnValue = implode($delimiter, $output);
-        } else {
-            $returnValue = $inputValue;
-        }
-        return $returnValue;
-    }
-
     /***********************************************
      *
      * Link functions (typolink)
@@ -5518,7 +5170,7 @@ class ContentObjectRenderer
         // 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 !== '') {
-            $linkHandlerObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]);
+            $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);
             }
@@ -5541,8 +5193,8 @@ class ContentObjectRenderer
                 // Resource was not found
                 return $linkText;
             }
-        // Disallow direct javascript: or data: links
         } elseif (in_array(strtolower(trim($linkHandlerKeyword)), ['javascript', 'data'], true)) {
+            // Disallow direct javascript: or data: links
             return $linkText;
         } else {
             $linkParameter = $linkParameterParts['url'];
@@ -5601,9 +5253,15 @@ class ContentObjectRenderer
 
         // Detecting kind of link and resolve all necessary parameters
         $linkService = GeneralUtility::makeInstance(LinkService::class);
-        $linkDetails = $linkService->resolve($linkParameter);
+        try {
+            $linkDetails = $linkService->resolve($linkParameter);
+        } catch (Exception\InvalidPathException $exception) {
+            $this->logger->warning('The link could not be generated', ['exception' => $exception]);
+            return $linkText;
+        }
+
         $linkDetails['typoLinkParameter'] = $linkParameter;
-        if (isset($GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']])) {
+        if (isset($linkDetails['type']) && isset($GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']])) {
             /** @var AbstractTypolinkBuilder $linkBuilder */
             $linkBuilder = GeneralUtility::makeInstance(
                 $GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']],
@@ -5612,9 +5270,13 @@ class ContentObjectRenderer
             try {
                 list($this->lastTypoLinkUrl, $linkText, $target) = $linkBuilder->build($linkDetails, $linkText, $target, $conf);
             } catch (UnableToLinkException $e) {
+                $this->logger->debug(sprintf('Unable to link "%s": %s', $e->getLinkText(), $e->getMessage()), ['exception' => $e]);
+
                 // Only return the link text directly
                 return $e->getLinkText();
             }
+        } elseif (isset($linkDetails['url'])) {
+            $this->lastTypoLinkUrl = $linkDetails['url'];
         } else {
             return $linkText;
         }
@@ -5674,8 +5336,8 @@ class ContentObjectRenderer
         // Target attribute
         if (!empty($target)) {
             $tagAttributes['target'] = htmlspecialchars($target);
-        // Create TARGET-attribute only if the right doctype is used
         } elseif ($JSwindowParams && !in_array($tsfe->xhtmlDoctype, ['xhtml_strict', 'xhtml_11'], true)) {
+            // Create TARGET-attribute only if the right doctype is used
             $tagAttributes['target'] = 'FEopenLink';
         }
 
@@ -5706,18 +5368,16 @@ class ContentObjectRenderer
         }
 
         // Hook: Call post processing function for link rendering:
-        if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc'])) {
-            $_params = [
-                'conf' => &$conf,
-                'linktxt' => &$linkText,
-                'finalTag' => &$finalAnchorTag,
-                'finalTagParts' => &$finalTagParts,
-                'linkDetails' => &$linkDetails,
-                'tagAttributes' => &$tagAttributes
-            ];
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc'] as $_funcRef) {
-                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
-            }
+        $_params = [
+            'conf' => &$conf,
+            'linktxt' => &$linkText,
+            'finalTag' => &$finalAnchorTag,
+            'finalTagParts' => &$finalTagParts,
+            'linkDetails' => &$linkDetails,
+            'tagAttributes' => &$tagAttributes
+        ];
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc'] ?? [] as $_funcRef) {
+            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
         }
 
         // If flag "returnLastTypoLinkUrl" set, then just return the latest URL made:
@@ -5858,19 +5518,16 @@ class ContentObjectRenderer
      * @param string $context The context in which the method is called (e.g. typoLink).
      * @param string $url The URL that should be processed.
      * @param array $typolinkConfiguration The current link configuration array.
-     * @return string|NULL Returns NULL if URL was not processed or the processed URL as a string.
+     * @return string|null Returns NULL if URL was not processed or the processed URL as a string.
      * @throws \RuntimeException if a hook was registered but did not fulfill the correct parameters.
      */
     protected function processUrl($context, $url, $typolinkConfiguration = [])
     {
-        if (
-            empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'])
-            || !is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'])
-        ) {
+        $urlProcessors = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'] ?? [];
+        if (empty($urlProcessors)) {
             return $url;
         }
 
-        $urlProcessors = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'];
         foreach ($urlProcessors as $identifier => $configuration) {
             if (empty($configuration) || !is_array($configuration)) {
                 throw new \RuntimeException('Missing configuration for URI processor "' . $identifier . '".', 1442050529);
@@ -5896,70 +5553,13 @@ class ContentObjectRenderer
     }
 
     /**
-     * Returns the &MP variable value for a page id.
-     * The function will do its best to find a MP value that will keep the page id inside the current Mount Point rootline if any.
-     *
-     * @param int $pageId page id
-     * @param bool $raw If TRUE, the MPvalue is returned raw. Normally it is encoded as &MP=... variable
-     * @return string MP value, prefixed with &MP= (depending on $raw)
-     * @see typolink()
-     */
-    public function getClosestMPvalueForPage($pageId, $raw = false)
-    {
-        $tsfe = $this->getTypoScriptFrontendController();
-        if (empty($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) || !$tsfe->MP) {
-            return '';
-        }
-        // MountPoints:
-        $MP = '';
-        // Same page as current.
-        if ((int)$tsfe->id === (int)$pageId) {
-            $MP = $tsfe->MP;
-        } else {
-            // ... otherwise find closest meeting point:
-            // Gets rootline of linked-to page
-            $tCR_rootline = $tsfe->sys_page->getRootLine($pageId, '', true);
-            $inverseTmplRootline = array_reverse($tsfe->tmpl->rootLine);
-            $rl_mpArray = [];
-            $startMPaccu = false;
-            // Traverse root line of link uid and inside of that the REAL root line of current position.
-            foreach ($tCR_rootline as $tCR_data) {
-                foreach ($inverseTmplRootline as $rlKey => $invTmplRLRec) {
-                    // Force accumulating when in overlay mode: Links to this page have to stay within the current branch
-                    if ($invTmplRLRec['_MOUNT_OL'] && (int)$tCR_data['uid'] === (int)$invTmplRLRec['uid']) {
-                        $startMPaccu = true;
-                    }
-                    // Accumulate MP data:
-                    if ($startMPaccu && $invTmplRLRec['_MP_PARAM']) {
-                        $rl_mpArray[] = $invTmplRLRec['_MP_PARAM'];
-                    }
-                    // If two PIDs matches and this is NOT the site root, start accumulation of MP data (on the next level):
-                    // (The check for site root is done so links to branches outsite the site but sharing the site roots PID
-                    // is NOT detected as within the branch!)
-                    if ((int)$tCR_data['pid'] === (int)$invTmplRLRec['pid'] && count($inverseTmplRootline) !== $rlKey + 1) {
-                        $startMPaccu = true;
-                    }
-                }
-                if ($startMPaccu) {
-                    // Good enough...
-                    break;
-                }
-            }
-            if (!empty($rl_mpArray)) {
-                $MP = implode(',', array_reverse($rl_mpArray));
-            }
-        }
-        return $raw ? $MP : ($MP ? '&MP=' . rawurlencode($MP) : '');
-    }
-
-    /**
      * Creates a href attibute for given $mailAddress.
      * The function uses spamProtectEmailAddresses for encoding the mailto statement.
      * If spamProtectEmailAddresses is disabled, it'll just return a string like "mailto:user@example.tld".
      *
      * @param string $mailAddress Email address
      * @param string $linktxt Link text, default will be the email address.
-     * @return string Returns a numerical array with two elements: 1) $mailToUrl, string ready to be inserted into the href attribute of the <a> tag, b) $linktxt: The string between starting and ending <a> tag.
+     * @return array A numerical array with two elements: 1) $mailToUrl, string ready to be inserted into the href attribute of the <a> tag, b) $linktxt: The string between starting and ending <a> tag.
      */
     public function getMailTo($mailAddress, $linktxt)
     {
@@ -6096,7 +5696,7 @@ class ContentObjectRenderer
      * Gets the query arguments and assembles them for URLs.
      * Arguments may be removed or set, depending on configuration.
      *
-     * @param string $conf Configuration
+     * @param array $conf Configuration
      * @param array $overruleQueryArguments Multidimensional key/value pairs that overrule incoming query arguments
      * @param bool $forceOverruleArguments If set, key/value pairs not in the query but the overrule array will be set
      * @return string The URL query part (starting with a &)
@@ -6130,11 +5730,7 @@ class ContentObjectRenderer
         } else {
             $newQueryArray = $currentQueryArray;
         }
-        if ($forceOverruleArguments) {
-            ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments);
-        } else {
-            ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments, false);
-        }
+        ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments, $forceOverruleArguments);
         return GeneralUtility::implodeArrayForUrl('', $newQueryArray, '', false, true);
     }
 
@@ -6185,39 +5781,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * Adds space above/below the input HTML string. It is done by adding a clear-gif and <br /> tag before and/or after the content.
-     *
-     * @param string $content The content to add space above/below to.
-     * @param string $wrap A value like "10 | 20" where the first part denotes the space BEFORE and the second part denotes the space AFTER (in pixels)
-     * @param array $conf Configuration from TypoScript
-     * @return string Wrapped string
-     */
-    public function wrapSpace($content, $wrap, array $conf = null)
-    {
-        if (trim($wrap)) {
-            $wrapArray = explode('|', $wrap);
-            $wrapBefore = (int)$wrapArray[0];
-            $wrapAfter = (int)$wrapArray[1];
-            $useDivTag = isset($conf['useDiv']) && $conf['useDiv'];
-            if ($wrapBefore) {
-                if ($useDivTag) {
-                    $content = '<div class="content-spacer spacer-before" style="height:' . $wrapBefore . 'px;"></div>' . $content;
-                } else {
-                    $content = '<span style="width: 1px; height: ' . $wrapBefore . 'px; display: inline-block;"></span><br />' . $content;
-                }
-            }
-            if ($wrapAfter) {
-                if ($useDivTag) {
-                    $content .= '<div class="content-spacer spacer-after" style="height:' . $wrapAfter . 'px;"></div>';
-                } else {
-                    $content .= '<span style="width: 1px; height: ' . $wrapAfter . 'px; display: inline-block;"></span><br />';
-                }
-            }
-        }
-        return $content;
-    }
-
-    /**
      * Calling a user function/class-method
      * Notice: For classes the instantiated object will have the internal variable, $cObj, set to be a *reference* to $this (the parent/calling object).
      *
@@ -6259,29 +5822,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * Parses a set of text lines with "[parameters] = [values]" into an array with parameters as keys containing the value
-     * If lines are empty or begins with "/" or "#" then they are ignored.
-     *
-     * @param string $params Text which the parameters
-     * @return array array with the parameters as key/value pairs
-     * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
-     */
-    public function processParams($params)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        $paramArr = [];
-        $lines = GeneralUtility::trimExplode(LF, $params, true);
-        foreach ($lines as $val) {
-            $pair = explode('=', $val, 2);
-            $key = trim($pair[0]);
-            if ($key[0] !== '#' && $key[0] !== '/') {
-                $paramArr[$key] = trim($pair[1]);
-            }
-        }
-        return $paramArr;
-    }
-
-    /**
      * Cleans up a string of keywords. Keywords at splitted by "," (comma)  ";" (semi colon) and linebreak
      *
      * @param string $content String of keywords
@@ -6306,7 +5846,6 @@ class ContentObjectRenderer
      */
     public function caseshift($theValue, $case)
     {
-        $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
         switch (strtolower($case)) {
             case 'upper':
                 $theValue = mb_strtoupper($theValue, 'utf-8');
@@ -6318,10 +5857,16 @@ class ContentObjectRenderer
                 $theValue = mb_convert_case($theValue, MB_CASE_TITLE, 'utf-8');
                 break;
             case 'ucfirst':
-                $theValue = $charsetConverter->convCaseFirst('utf-8', $theValue, 'toUpper');
+                $firstChar = mb_substr($theValue, 0, 1, 'utf-8');
+                $firstChar = mb_strtoupper($firstChar, 'utf-8');
+                $remainder = mb_substr($theValue, 1, null, 'utf-8');
+                $theValue = $firstChar . $remainder;
                 break;
             case 'lcfirst':
-                $theValue = $charsetConverter->convCaseFirst('utf-8', $theValue, 'toLower');
+                $firstChar = mb_substr($theValue, 0, 1, 'utf-8');
+                $firstChar = mb_strtolower($firstChar, 'utf-8');
+                $remainder = mb_substr($theValue, 1, null, 'utf-8');
+                $theValue = $firstChar . $remainder;
                 break;
             case 'uppercamelcase':
                 $theValue = GeneralUtility::underscoredToUpperCamelCase($theValue);
@@ -6457,45 +6002,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * Checks if $url has a '?' in it and if not, a '?' is inserted between $url and $params, which are anyway concatenated and returned
-     *
-     * @param string $url Input URL
-     * @param string $params URL parameters
-     * @return string
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use this functionality by yourself instead of using cObj for that
-     */
-    public function URLqMark($url, $params)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        if ($params && !strstr($url, '?')) {
-            return $url . '?' . $params;
-        } else {
-            return $url . $params;
-        }
-    }
-
-    /**
-     * Clears TypoScript properties listed in $propList from the input TypoScript array.
-     *
-     * @param array $TSArr TypoScript array of values/properties
-     * @param string $propList List of properties to clear both value/properties for. Eg. "myprop,another_property
-     * @return array The TypoScript array
-     * @see gifBuilderTextBox()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, as the textbox is built within GifBuilder
-     */
-    public function clearTSProperties($TSArr, $propList)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        $list = explode(',', $propList);
-        foreach ($list as $prop) {
-            $prop = trim($prop);
-            unset($TSArr[$prop]);
-            unset($TSArr[$prop . '.']);
-        }
-        return $TSArr;
-    }
-
-    /**
      * Resolves a TypoScript reference value to the full set of properties BUT overridden with any local properties set.
      * So the reference is resolved but overlaid with local TypoScript properties of the reference value.
      *
@@ -6519,108 +6025,6 @@ class ContentObjectRenderer
         return $confArr;
     }
 
-    /**
-     * This function creates a number of TEXT-objects in a Gifbuilder configuration in order to create a text-field like thing.
-     *
-     * @param array $gifbuilderConf TypoScript properties for Gifbuilder - TEXT GIFBUILDER objects are added to this array and returned.
-     * @param array $conf TypoScript properties for this function
-     * @param string $text The text string to write onto the GIFBUILDER file
-     * @return array The modified $gifbuilderConf array
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, as the textbox is built within GifBuilder
-     */
-    public function gifBuilderTextBox($gifbuilderConf, $conf, $text)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        $chars = (int)$conf['chars'] ?: 20;
-        $lineDist = (int)$conf['lineDist'] ?: 20;
-        $Valign = strtolower(trim($conf['Valign']));
-        $tmplObjNumber = (int)$conf['tmplObjNumber'];
-        $maxLines = (int)$conf['maxLines'];
-        if ($tmplObjNumber && $gifbuilderConf[$tmplObjNumber] === 'TEXT') {
-            $textArr = $this->linebreaks($text, $chars, $maxLines);
-            $angle = (int)$gifbuilderConf[$tmplObjNumber . '.']['angle'];
-            foreach ($textArr as $c => $textChunk) {
-                $index = $tmplObjNumber + 1 + $c * 2;
-                // Workarea
-                $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $index);
-                $rad_angle = 2 * pi() / 360 * $angle;
-                $x_d = sin($rad_angle) * $lineDist;
-                $y_d = cos($rad_angle) * $lineDist;
-                $diff_x_d = 0;
-                $diff_y_d = 0;
-                if ($Valign === 'center') {
-                    $diff_x_d = $x_d * count($textArr);
-                    $diff_x_d = $diff_x_d / 2;
-                    $diff_y_d = $y_d * count($textArr);
-                    $diff_y_d = $diff_y_d / 2;
-                }
-                $x_d = round($x_d * $c - $diff_x_d);
-                $y_d = round($y_d * $c - $diff_y_d);
-                $gifbuilderConf[$index] = 'WORKAREA';
-                $gifbuilderConf[$index . '.']['set'] = $x_d . ',' . $y_d;
-                // Text
-                $index++;
-                $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $index);
-                $gifbuilderConf[$index] = 'TEXT';
-                $gifbuilderConf[$index . '.'] = $this->clearTSProperties($gifbuilderConf[$tmplObjNumber . '.'], 'text');
-                $gifbuilderConf[$index . '.']['text'] = $textChunk;
-            }
-            $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $tmplObjNumber);
-        }
-        return $gifbuilderConf;
-    }
-
-    /**
-     * Splits a text string into lines and returns an array with these lines but a max number of lines.
-     *
-     * @param string $string The string to break
-     * @param int $chars Max number of characters per line.
-     * @param int $maxLines Max number of lines in all.
-     * @return array array with lines.
-     * @access private
-     * @see gifBuilderTextBox()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, as the textbox is built within GifBuilder
-     */
-    public function linebreaks($string, $chars, $maxLines = 0)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        $lines = explode(LF, $string);
-        $lineArr = [];
-        $c = 0;
-        foreach ($lines as $paragraph) {
-            $words = explode(' ', $paragraph);
-            foreach ($words as $word) {
-                if (strlen($lineArr[$c] . $word) > $chars) {
-                    $c++;
-                }
-                if (!$maxLines || $c < $maxLines) {
-                    $lineArr[$c] .= $word . ' ';
-                }
-            }
-            $c++;
-        }
-        return $lineArr;
-    }
-
-    /**
-     * Includes resources if the config property 'includeLibs' is set.
-     *
-     * @param array $config TypoScript configuration
-     * @return bool Whether a configuration for including libs was found and processed
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, use proper class loading instead.
-     */
-    public function includeLibs(array $config)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        $librariesIncluded = false;
-        if (isset($config['includeLibs']) && $config['includeLibs']) {
-            $libraries = GeneralUtility::trimExplode(',', $config['includeLibs'], true);
-            $this->getTypoScriptFrontendController()->includeLibraries($libraries);
-            $librariesIncluded = true;
-        }
-        return $librariesIncluded;
-    }
-
     /***********************************************
      *
      * Database functions, making of queries
@@ -6644,9 +6048,7 @@ class ContentObjectRenderer
      */
     public function enableFields($table, $show_hidden = false, array $ignore_array = [])
     {
-        $tsfe = $this->getTypoScriptFrontendController();
-        $show_hidden = $show_hidden ?: ($table === 'pages' ? $tsfe->showHiddenPage : $tsfe->showHiddenRecords);
-        return $tsfe->sys_page->enableFields($table, (bool)$show_hidden, $ignore_array);
+        return $this->getTypoScriptFrontendController()->sys_page->enableFields($table, $show_hidden ? true : -1, $ignore_array);
     }
 
     /**
@@ -6703,7 +6105,7 @@ class ContentObjectRenderer
                 $addSelectFields,
                 $moreWhereClauses,
                 $prevId_array,
-                $tsfe->gr_list
+                GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('frontend.user', 'groupIds', [0, -1])
             ];
             $requestHash = md5(serialize($parameters));
             $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
@@ -6847,9 +6249,16 @@ class ContentObjectRenderer
                             $theList = array_merge(
                                 GeneralUtility::intExplode(
                                     ',',
-                                    $this->getTreeList($next_id, $depth - 1, $begin - 1,
-                                        $dontCheckEnableFields, $addSelectFields, $moreWhereClauses,
-                                        $prevId_array, $recursionLevel + 1),
+                                    $this->getTreeList(
+                                        $next_id,
+                                        $depth - 1,
+                                        $begin - 1,
+                                        $dontCheckEnableFields,
+                                        $addSelectFields,
+                                        $moreWhereClauses,
+                                        $prevId_array,
+                                        $recursionLevel + 1
+                                    ),
                                     true
                                 ),
                                 $theList
@@ -6891,23 +6300,14 @@ class ContentObjectRenderer
      * @param string $searchTable The table name you search in (recommended for DBAL compliance. Will be prepended field names as well)
      * @return string The WHERE clause.
      */
-    public function searchWhere($searchWords, $searchFieldList, $searchTable = '')
+    public function searchWhere($searchWords, $searchFieldList, $searchTable)
     {
         if (!$searchWords) {
             return ' AND 1=1';
         }
 
-        if (empty($searchTable)) {
-            GeneralUtility::deprecationLog(
-                'Parameter 3 of ContentObjectRenderer::searchWhere() is required can not be omitted anymore. Using Default connection!'
-            );
-            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                ->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME)
-                ->createQueryBuilder();
-        } else {
-            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                ->getQueryBuilderForTable($searchTable);
-        }
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable($searchTable);
 
         $prefixTableName = $searchTable ? $searchTable . '.' : '';
 
@@ -6923,10 +6323,7 @@ class ContentObjectRenderer
             $searchWord = $queryBuilder->escapeLikeWildcards($searchWord);
             foreach ($searchFields as $field) {
                 $searchWordConstraint->add(
-                    $queryBuilder->expr()->like(
-                        $prefixTableName . $field,
-                        $queryBuilder->createNamedParameter('%' . $searchWord . '%', \PDO::PARAM_STR)
-                    )
+                    $queryBuilder->expr()->like($prefixTableName . $field, $queryBuilder->quote('%' . $searchWord . '%'))
                 );
             }
 
@@ -7031,9 +6428,10 @@ class ContentObjectRenderer
             'where'
         ];
         foreach ($properties as $property) {
-            $conf[$property] = trim(isset($conf[$property . '.'])
-                ? $this->stdWrap($conf[$property], $conf[$property . '.'])
-                : $conf[$property]
+            $conf[$property] = trim(
+                isset($conf[$property . '.'])
+                    ? $this->stdWrap($conf[$property], $conf[$property . '.'])
+                    : $conf[$property]
             );
             if ($conf[$property] === '') {
                 unset($conf[$property]);
@@ -7314,7 +6712,7 @@ class ContentObjectRenderer
         ];
 
         $considerMovePlaceholders = (
-            $tsfe->sys_page->versioningPreview && $table !== 'pages'
+            $tsfe->sys_page->versioningWorkspaceId > 0 && $table !== 'pages'
             && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
         );
 
@@ -7395,8 +6793,7 @@ class ContentObjectRenderer
                 // 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'];
+                    $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation'];
                 if (trim($includeRecordsWithoutDefaultTranslation) !== '') {
                     $languageQuery = $expressionBuilder->orX(
                         $languageQuery,
@@ -7446,64 +6843,6 @@ class ContentObjectRenderer
     }
 
     /**
-     * Helper function for getQuery(), creating the WHERE clause of the SELECT query
-     *
-     * @param string $table The table name
-     * @param array $conf The TypoScript configuration properties
-     * @param bool $returnQueryArray If set, the function will return the query not as a string but array with the various parts. RECOMMENDED!
-     * @return mixed A WHERE clause based on the relevant parts of the TypoScript properties for a "select" function in TypoScript, see link. If $returnQueryArray is FALSE the where clause is returned as a string with WHERE, GROUP BY and ORDER BY parts, otherwise as an array with these parts.
-     * @access private
-     * @see getQuery()
-     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
-     */
-    public function getWhere($table, $conf, $returnQueryArray = false)
-    {
-        GeneralUtility::logDeprecatedFunction();
-        // Init:
-        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
-        $queryConstraints = $this->getQueryConstraints($table, $conf);
-        $query = '';
-
-        $queryParts = [
-            'SELECT' => '',
-            'FROM' => '',
-            'WHERE' => '',
-            'GROUPBY' => '',
-            'ORDERBY' => '',
-            'LIMIT' => ''
-        ];
-
-        // MAKE WHERE:
-        if (!empty($queryConstraints['where'])) {
-            $queryParts['WHERE'] = (string)$queryConstraints['where'];
-            $query = 'WHERE ' . $queryParts['WHERE'];
-        }
-
-        // GROUP BY
-        if (!empty($queryConstraints['groupBy'])) {
-            $queryParts['GROUPBY'] = implode(
-                ', ',
-                array_map([$queryBuilder, 'quoteIdentifier'], $queryConstraints['groupBy'])
-            );
-            $query .= ' GROUP BY ' . $queryParts['GROUPBY'];
-        }
-
-        // ORDER BY
-        if (!empty($queryConstraints['orderBy'])) {
-            $orderBy = [];
-            foreach ($queryConstraints['orderBy'] as $orderPair) {
-                list($fieldName, $direction) = $orderPair;
-                $orderBy[] = trim($queryBuilder->quoteIdentifier($fieldName) . ' ' . $direction);
-            }
-            $queryParts['ORDERBY'] = implode(', ', $orderBy);
-            $query .= ' ORDER BY ' . $queryParts['ORDERBY'];
-        }
-
-        // Return result:
-        return $returnQueryArray ? $queryParts : $query;
-    }
-
-    /**
      * Helper function for getQuery, sanitizing the select part
      *
      * This functions checks if the necessary fields are part of the select
@@ -7524,7 +6863,7 @@ class ContentObjectRenderer
         $matchEnd = '(\\s*,|\\s*$)/';
         $necessaryFields = ['uid', 'pid'];
         $wsFields = ['t3ver_state'];
-        if (isset($GLOBALS['TCA'][$table]) && !preg_match(($matchStart . '\\*' . $matchEnd), $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)/i', $selectPart)) {
+        if (isset($GLOBALS['TCA'][$table]) && !preg_match($matchStart . '\\*' . $matchEnd, $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)/i', $selectPart)) {
             foreach ($necessaryFields as $field) {
                 $match = $matchStart . $field . $matchEnd;
                 if (!preg_match($match, $selectPart)) {
@@ -7549,7 +6888,7 @@ class ContentObjectRenderer
      * @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
-     * @see getWhere(),checkPid()
+     * @see checkPid()
      */
     public function checkPidArray($listArr)
     {
@@ -7592,7 +6931,7 @@ class ContentObjectRenderer
      * @param int $uid Page UID to test
      * @return bool TRUE if OK
      * @access private
-     * @see getWhere(), checkPidArray()
+     * @see checkPidArray()
      */
     public function checkPid($uid)
     {
@@ -7659,7 +6998,7 @@ class ContentObjectRenderer
                     // Handle float
                     $markerValues[$marker] = (float)$tempValue;
                 }
-            } elseif (is_null($tempValue)) {
+            } elseif ($tempValue === null) {
                 // It represents NULL
                 $markerValues[$marker] = 'NULL';
             } elseif (!empty($conf['markers.'][$dottedMarker]['commaSeparatedList'])) {
@@ -7717,7 +7056,7 @@ class ContentObjectRenderer
      */
     public function editPanel($content, $conf, $currentRecord = '', $dataArr = [])
     {
-        if ($this->getTypoScriptFrontendController()->beUserLogin && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
+        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
             if (!$currentRecord) {
                 $currentRecord = $this->currentRecord;
             }
@@ -7744,7 +7083,7 @@ class ContentObjectRenderer
      */
     public function editIcons($content, $params, array $conf = [], $currentRecord = '', $dataArr = [], $addUrlParamStr = '')
     {
-        if ($this->getTypoScriptFrontendController()->beUserLogin && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
+        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
             if (!$currentRecord) {
                 $currentRecord = $this->currentRecord;
             }
@@ -7811,7 +7150,7 @@ class ContentObjectRenderer
 
         $cacheKey = $this->calculateCacheKey($configuration);
         if (!empty($cacheKey)) {
-            /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend */
+            /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
             $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)
                 ->getCache('cache_hash');
             $content = $cacheFrontend->get($cacheKey);
@@ -7827,9 +7166,7 @@ class ContentObjectRenderer
      */
     protected function calculateCacheLifetime(array $configuration)
     {
-        $lifetimeConfiguration = isset($configuration['lifetime'])
-            ? $configuration['lifetime']
-            : '';
+        $lifetimeConfiguration = $configuration['lifetime'] ?? '';
         $lifetimeConfiguration = isset($configuration['lifetime.'])
             ? $this->stdWrap($lifetimeConfiguration, $configuration['lifetime.'])
             : $lifetimeConfiguration;
@@ -7851,7 +7188,7 @@ class ContentObjectRenderer
      */
     protected function calculateCacheTags(array $configuration)
     {
-        $tags = isset($configuration['tags']) ? $configuration['tags'] : '';
+        $tags = $configuration['tags'] ?? '';
         $tags = isset($configuration['tags.'])
             ? $this->stdWrap($tags, $configuration['tags.'])
             : $tags;
@@ -7866,7 +7203,7 @@ class ContentObjectRenderer
      */
     protected function calculateCacheKey(array $configuration)
     {
-        $key = isset($configuration['key']) ? $configuration['key'] : '';
+        $key = $configuration['key'] ?? '';
         return isset($configuration['key.'])
             ? $this->stdWrap($key, $configuration['key.'])
             : $key;