[TASK] Deprecate GraphicalFunctions->init method
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / ContentObjectRenderer.php
index 8262ea1..766fca4 100644 (file)
@@ -16,8 +16,12 @@ namespace TYPO3\CMS\Frontend\ContentObject;
 
 use Doctrine\DBAL\DBALException;
 use Doctrine\DBAL\Driver\Statement;
 
 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\Cache\CacheManager;
-use TYPO3\CMS\Core\Charset\CharsetConverter;
+use TYPO3\CMS\Core\Context\Context;
+use TYPO3\CMS\Core\Context\LanguageAspect;
+use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
@@ -26,6 +30,8 @@ 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\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\LinkHandling\LinkService;
 use TYPO3\CMS\Core\Log\LogManager;
 use TYPO3\CMS\Core\Mail\MailMessage;
@@ -37,29 +43,32 @@ 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\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\DependencyOrderingService;
+use TYPO3\CMS\Core\Service\FlexFormService;
 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
+use TYPO3\CMS\Core\TypoScript\TypoScriptService;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\DebugUtility;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\DebugUtility;
-use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\HttpUtility;
 use TYPO3\CMS\Core\Utility\MailUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Core\Utility\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\Core\Utility\StringUtility;
 use TYPO3\CMS\Core\Versioning\VersionState;
-use TYPO3\CMS\Extbase\Service\FlexFormService;
 use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
 use TYPO3\CMS\Frontend\ContentObject\Exception\ExceptionHandlerInterface;
 use TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3\CMS\Frontend\Http\UrlProcessorInterface;
 use TYPO3\CMS\Frontend\Imaging\GifBuilder;
 use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
 use TYPO3\CMS\Frontend\ContentObject\Exception\ExceptionHandlerInterface;
 use TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3\CMS\Frontend\Http\UrlProcessorInterface;
 use TYPO3\CMS\Frontend\Imaging\GifBuilder;
-use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
 use TYPO3\CMS\Frontend\Page\PageRepository;
 use TYPO3\CMS\Frontend\Page\PageRepository;
+use TYPO3\CMS\Frontend\Resource\FilePathSanitizer;
 use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
+use TYPO3\CMS\Frontend\Typolink\AbstractTypolinkBuilder;
+use TYPO3\CMS\Frontend\Typolink\UnableToLinkException;
 
 /**
  * This class contains all main TypoScript features.
 
 /**
  * This class contains all main TypoScript features.
@@ -70,8 +79,10 @@ use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
  * 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.
  */
  * 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
      */
     /**
      * @var array
      */
@@ -171,8 +182,6 @@ class ContentObjectRenderer
         'bytes.' => 'array',
         'substring' => 'parameters',
         'substring.' => 'array',
         'bytes.' => 'array',
         'substring' => 'parameters',
         'substring.' => 'array',
-        'removeBadHTML' => 'boolean',
-        'removeBadHTML.' => 'array',
         'cropHTML' => 'crop',
         'cropHTML.' => 'array',
         'stripHtml' => 'boolean',
         'cropHTML' => 'crop',
         'cropHTML.' => 'array',
         'stripHtml' => 'boolean',
@@ -198,8 +207,6 @@ class ContentObjectRenderer
         'innerWrap.' => 'array',
         'innerWrap2' => 'wrap',
         'innerWrap2.' => 'array',
         'innerWrap.' => 'array',
         'innerWrap2' => 'wrap',
         'innerWrap2.' => 'array',
-        'fontTag' => 'wrap',
-        'fontTag.' => 'array',
         'addParams.' => 'array',
         'filelink.' => 'array',
         'preCObject' => 'cObject',
         'addParams.' => 'array',
         'filelink.' => 'array',
         'preCObject' => 'cObject',
@@ -209,13 +216,6 @@ class ContentObjectRenderer
         'wrapAlign' => 'align',
         'wrapAlign.' => 'array',
         'typolink.' => 'array',
         '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',
         'wrap' => 'wrap',
         'wrap.' => 'array',
         'noTrimWrap' => 'wrap',
@@ -264,121 +264,6 @@ class ContentObjectRenderer
     protected $contentObjectClassMap = [];
 
     /**
     protected $contentObjectClassMap = [];
 
     /**
-     * Holds ImageMagick parameters and extensions used for compression
-     *
-     * @var array
-     * @see IMGTEXT()
-     */
-    public $image_compression = [
-        10 => [
-            'params' => '',
-            'ext' => 'gif'
-        ],
-        11 => [
-            'params' => '-colors 128',
-            'ext' => 'gif'
-        ],
-        12 => [
-            'params' => '-colors 64',
-            'ext' => 'gif'
-        ],
-        13 => [
-            'params' => '-colors 32',
-            'ext' => 'gif'
-        ],
-        14 => [
-            'params' => '-colors 16',
-            'ext' => 'gif'
-        ],
-        15 => [
-            'params' => '-colors 8',
-            'ext' => 'gif'
-        ],
-        20 => [
-            'params' => '-quality 100',
-            'ext' => 'jpg'
-        ],
-        21 => [
-            'params' => '-quality 90',
-            'ext' => 'jpg'
-        ],
-        22 => [
-            'params' => '-quality 80',
-            'ext' => 'jpg'
-        ],
-        23 => [
-            'params' => '-quality 70',
-            'ext' => 'jpg'
-        ],
-        24 => [
-            'params' => '-quality 60',
-            'ext' => 'jpg'
-        ],
-        25 => [
-            'params' => '-quality 50',
-            'ext' => 'jpg'
-        ],
-        26 => [
-            'params' => '-quality 40',
-            'ext' => 'jpg'
-        ],
-        27 => [
-            'params' => '-quality 30',
-            'ext' => 'jpg'
-        ],
-        28 => [
-            'params' => '-quality 20',
-            'ext' => 'jpg'
-        ],
-        30 => [
-            'params' => '-colors 256',
-            'ext' => 'png'
-        ],
-        31 => [
-            'params' => '-colors 128',
-            'ext' => 'png'
-        ],
-        32 => [
-            'params' => '-colors 64',
-            'ext' => 'png'
-        ],
-        33 => [
-            'params' => '-colors 32',
-            'ext' => 'png'
-        ],
-        34 => [
-            'params' => '-colors 16',
-            'ext' => 'png'
-        ],
-        35 => [
-            'params' => '-colors 8',
-            'ext' => 'png'
-        ],
-        39 => [
-            'params' => '',
-            'ext' => 'png'
-        ]
-    ];
-
-    /**
-     * ImageMagick parameters for image effects
-     *
-     * @var array
-     * @see IMGTEXT()
-     */
-    public $image_effects = [
-        1 => '-rotate 90',
-        2 => '-rotate 270',
-        3 => '-rotate 180',
-        10 => '-colorspace GRAY',
-        11 => '-sharpen 70',
-        20 => '-normalize',
-        23 => '-contrast',
-        25 => '-gamma 1.3',
-        26 => '-gamma 0.8'
-    ];
-
-    /**
      * Loaded with the current data-record.
      *
      * If the instance of this class is used to render records from the database those records are found in this array.
      * Loaded with the current data-record.
      *
      * If the instance of this class is used to render records from the database those records are found in this array.
@@ -488,13 +373,6 @@ class ContentObjectRenderer
     public $lastTypoLinkLD = [];
 
     /**
     public $lastTypoLinkLD = [];
 
     /**
-     * Caching substituteMarkerArrayCached function
-     *
-     * @var array
-     */
-    public $substMarkerCache = [];
-
-    /**
      * array that registers rendered content elements (or any table) to make sure they are not rendered recursively!
      *
      * @var array
      * array that registers rendered content elements (or any table) to make sure they are not rendered recursively!
      *
      * @var array
@@ -530,7 +408,7 @@ class ContentObjectRenderer
     /**
      * @var File Current file objects (during iterations over files)
      */
     /**
      * @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
 
     /**
      * Set to TRUE by doConvertToUserIntObject() if USER object wants to become USER_INT
@@ -560,11 +438,6 @@ class ContentObjectRenderer
     protected $typoScriptFrontendController;
 
     /**
     protected $typoScriptFrontendController;
 
     /**
-     * @var MarkerBasedTemplateService
-     */
-    protected $templateService;
-
-    /**
      * Indicates that object type is USER.
      *
      * @see ContentObjectRender::$userObjectType
      * Indicates that object type is USER.
      *
      * @see ContentObjectRender::$userObjectType
@@ -584,7 +457,6 @@ class ContentObjectRenderer
     {
         $this->typoScriptFrontendController = $typoScriptFrontendController;
         $this->contentObjectClassMap = $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'];
     {
         $this->typoScriptFrontendController = $typoScriptFrontendController;
         $this->contentObjectClassMap = $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'];
-        $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
     }
 
     /**
     }
 
     /**
@@ -597,7 +469,7 @@ class ContentObjectRenderer
     public function __sleep()
     {
         $vars = get_object_vars($this);
     public function __sleep()
     {
         $vars = get_object_vars($this);
-        unset($vars['typoScriptFrontendController']);
+        unset($vars['typoScriptFrontendController'], $vars['logger']);
         if ($this->currentFile instanceof FileReference) {
             $this->currentFile = 'FileReference:' . $this->currentFile->getUid();
         } elseif ($this->currentFile instanceof File) {
         if ($this->currentFile instanceof FileReference) {
             $this->currentFile = 'FileReference:' . $this->currentFile->getUid();
         } elseif ($this->currentFile instanceof File) {
@@ -630,6 +502,7 @@ class ContentObjectRenderer
                 $this->currentFile = null;
             }
         }
                 $this->currentFile = null;
             }
         }
+        $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
     }
 
     /**
     }
 
     /**
@@ -652,9 +525,9 @@ class ContentObjectRenderer
      * This method is private API, please use configuration
      * $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'] to add new content objects
      *
      * This method is private API, please use configuration
      * $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'] to add new content objects
      *
-     * @internal
      * @param string $className
      * @param string $contentObjectName
      * @param string $className
      * @param string $contentObjectName
+     * @internal
      */
     public function registerContentObjectClass($className, $contentObjectName)
     {
      */
     public function registerContentObjectClass($className, $contentObjectName)
     {
@@ -668,37 +541,32 @@ class ContentObjectRenderer
      *
      * @param array $data The record data that is rendered.
      * @param string $table The table that the data record is from.
      *
      * @param array $data The record data that is rendered.
      * @param string $table The table that the data record is from.
-     * @return void
      */
     public function start($data, $table = '')
     {
         $this->data = $data;
         $this->table = $table;
      */
     public function start($data, $table = '')
     {
         $this->data = $data;
         $this->table = $table;
-        $this->currentRecord = $table !== '' ? $table . ':' . $this->data['uid'] : '';
+        $this->currentRecord = $table !== ''
+            ? $table . ':' . ($this->data['uid'] ?? '')
+            : '';
         $this->parameters = [];
         $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 = [];
         }
         $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);
         }
     }
 
         }
     }
 
@@ -722,14 +590,12 @@ class ContentObjectRenderer
     {
         if (!isset($this->getImgResourceHookObjects)) {
             $this->getImgResourceHookObjects = [];
     {
         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;
             }
         }
         return $this->getImgResourceHookObjects;
@@ -741,7 +607,6 @@ class ContentObjectRenderer
      *
      * @param array $data The record array
      * @param string $currentRecord This is set to the [table]:[uid] of the record delivered in the $data-array, if the cObjects CONTENT or RECORD is in operation. Note that $GLOBALS['TSFE']->currentRecord is set to an equal value but always indicating the latest record rendered.
      *
      * @param array $data The record array
      * @param string $currentRecord This is set to the [table]:[uid] of the record delivered in the $data-array, if the cObjects CONTENT or RECORD is in operation. Note that $GLOBALS['TSFE']->currentRecord is set to an equal value but always indicating the latest record rendered.
-     * @return void
      * @access private
      */
     public function setParent($data, $currentRecord)
      * @access private
      */
     public function setParent($data, $currentRecord)
@@ -774,7 +639,6 @@ class ContentObjectRenderer
      * Sets the "current" value.
      *
      * @param mixed $value The variable that you want to set as "current
      * Sets the "current" value.
      *
      * @param mixed $value The variable that you want to set as "current
-     * @return void
      * @see getCurrentVal()
      */
     public function setCurrentVal($value)
      * @see getCurrentVal()
      */
     public function setCurrentVal($value)
@@ -829,7 +693,7 @@ class ContentObjectRenderer
                 $timeTracker->push($TSkey, $name);
             }
             // Checking if the COBJ is a reference to another object. (eg. name of 'blabla.blabla = < styles.something')
                 $timeTracker->push($TSkey, $name);
             }
             // Checking if the COBJ is a reference to another object. (eg. name of 'blabla.blabla = < styles.something')
-            if ($name[0] === '<') {
+            if (isset($name[0]) && $name[0] === '<') {
                 $key = trim(substr($name, 1));
                 $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
                 // $name and $conf is loaded with the referenced values.
                 $key = trim(substr($name, 1));
                 $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
                 // $name and $conf is loaded with the referenced values.
@@ -845,7 +709,7 @@ class ContentObjectRenderer
                 // Application defined cObjects
                 if (!empty($this->cObjHookObjectsRegistry[$name])) {
                     if (empty($this->cObjHookObjectsArr[$name])) {
                 // 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')) {
                     }
                     $hookObj = $this->cObjHookObjectsArr[$name];
                     if (method_exists($hookObj, 'cObjGetSingleExt')) {
@@ -859,9 +723,9 @@ class ContentObjectRenderer
                         $content .= $this->render($contentObject, $conf);
                     } else {
                         // Call hook functions for extra processing
                         $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);
                                 }
                                 if (!$hookObject instanceof ContentObjectGetSingleHookInterface) {
                                     throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetSingleHookInterface::class, 1195043731);
                                 }
@@ -891,7 +755,7 @@ class ContentObjectRenderer
      * in $this->contentObjectClassMap
      *
      * @param string $name
      * in $this->contentObjectClassMap
      *
      * @param string $name
-     * @return NULL|AbstractContentObject
+     * @return AbstractContentObject|null
      * @throws ContentRenderingException
      */
     public function getContentObject($name)
      * @throws ContentRenderingException
      */
     public function getContentObject($name)
@@ -929,7 +793,7 @@ class ContentObjectRenderer
         $content = '';
 
         // Evaluate possible cache and return
         $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);
         if ($cacheConfiguration !== null) {
             unset($configuration['cache.']);
             $cache = $this->getFromCache($cacheConfiguration);
@@ -949,16 +813,15 @@ class ContentObjectRenderer
             $exceptionHandler = $this->createExceptionHandler($configuration);
             if ($exceptionHandler === null) {
                 throw $exception;
             $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)) {
         }
 
         // 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);
                 $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
                 $tags = $this->calculateCacheTags($cacheConfiguration);
                 $lifetime = $this->calculateCacheLifetime($cacheConfiguration);
@@ -974,7 +837,7 @@ class ContentObjectRenderer
      * or, from global configuration if not explicitly disabled in local configuration
      *
      * @param array $configuration
      * 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 = [])
      * @throws ContentRenderingException
      */
     protected function createExceptionHandler($configuration = [])
@@ -995,7 +858,7 @@ class ContentObjectRenderer
      * Determine exception handler class name from global and content object configuration
      *
      * @param array $configuration
      * Determine exception handler class name from global and content object configuration
      *
      * @param array $configuration
-     * @return string|NULL
+     * @return string|null
      */
     protected function determineExceptionHandlerClassName($configuration)
     {
      */
     protected function determineExceptionHandlerClassName($configuration)
     {
@@ -1058,7 +921,6 @@ class ContentObjectRenderer
      * Sets the user object type
      *
      * @param mixed $userObjectType
      * Sets the user object type
      *
      * @param mixed $userObjectType
-     * @return void
      */
     public function setUserObjectType($userObjectType)
     {
      */
     public function setUserObjectType($userObjectType)
     {
@@ -1067,8 +929,6 @@ class ContentObjectRenderer
 
     /**
      * Requests the current USER object to be converted to USER_INT.
 
     /**
      * Requests the current USER object to be converted to USER_INT.
-     *
-     * @return void
      */
     public function convertToUserIntObject()
     {
      */
     public function convertToUserIntObject()
     {
@@ -1090,7 +950,6 @@ class ContentObjectRenderer
      * @param string|array $flexData Flexform data
      * @param array $conf Array to write the data into, by reference
      * @param bool $recursive Is set if called recursive. Don't call function with this parameter, it's used inside the function only
      * @param string|array $flexData Flexform data
      * @param array $conf Array to write the data into, by reference
      * @param bool $recursive Is set if called recursive. Don't call function with this parameter, it's used inside the function only
-     * @return void
      */
     public function readFlexformIntoConf($flexData, &$conf, $recursive = false)
     {
      */
     public function readFlexformIntoConf($flexData, &$conf, $recursive = false)
     {
@@ -1181,7 +1040,7 @@ class ContentObjectRenderer
         if (!is_array($info)) {
             return '';
         }
         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];
             $source = $tsfe->absRefPrefix . str_replace('%2F', '/', rawurlencode($info['3']));
         } else {
             $source = $info[3];
@@ -1207,10 +1066,11 @@ class ContentObjectRenderer
             'altParams' => $altParam,
             'border' =>  $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
             'sourceCollection' => $sourceCollection,
             'altParams' => $altParam,
             'border' =>  $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
             'sourceCollection' => $sourceCollection,
-            'selfClosingTagSlash' => (!empty($tsfe->xhtmlDoctype) ? ' /' : ''),
+            'selfClosingTagSlash' => !empty($tsfe->xhtmlDoctype) ? ' /' : '',
         ];
 
         ];
 
-        $theValue = $this->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
+        $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
+        $theValue = $markerTemplateEngine->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
 
         $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
         if ($linkWrap) {
 
         $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
         if ($linkWrap) {
@@ -1258,7 +1118,10 @@ class ContentObjectRenderer
     public function getImageTagTemplate($layoutKey, $conf)
     {
         if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
     public function getImageTagTemplate($layoutKey, $conf)
     {
         if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
-            $imageTagLayout = $this->stdWrap($conf['layout.'][$layoutKey . '.']['element'], $conf['layout.'][$layoutKey . '.']['element.']);
+            $imageTagLayout = $this->stdWrap(
+                $conf['layout.'][$layoutKey . '.']['element'] ?? '',
+                $conf['layout.'][$layoutKey . '.']['element.'] ?? []
+            );
         } else {
             $imageTagLayout = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
         }
         } else {
             $imageTagLayout = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
         }
@@ -1277,7 +1140,13 @@ class ContentObjectRenderer
     public function getImageSourceCollection($layoutKey, $conf, $file)
     {
         $sourceCollection = '';
     public function getImageSourceCollection($layoutKey, $conf, $file)
     {
         $sourceCollection = '';
-        if ($layoutKey && $conf['sourceCollection.'] && ($conf['layout.'][$layoutKey . '.']['source'] || $conf['layout.'][$layoutKey . '.']['source.'])) {
+        if ($layoutKey
+            && isset($conf['sourceCollection.']) && $conf['sourceCollection.']
+            && (
+                isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source']
+                || isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.']
+            )
+        ) {
 
             // find active sourceCollection
             $activeSourceCollections = [];
 
             // find active sourceCollection
             $activeSourceCollections = [];
@@ -1291,19 +1160,23 @@ class ContentObjectRenderer
 
             // apply option split to configurations
             $tsfe = $this->getTypoScriptFrontendController();
 
             // apply option split to configurations
             $tsfe = $this->getTypoScriptFrontendController();
-            $srcLayoutOptionSplitted = $tsfe->tmpl->splitConfArray($conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
+            $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
+            $srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
 
             // render sources
             foreach ($activeSourceCollections as $key => $sourceConfiguration) {
 
             // render sources
             foreach ($activeSourceCollections as $key => $sourceConfiguration) {
-                $sourceLayout = $this->stdWrap($srcLayoutOptionSplitted[$key]['source'], $srcLayoutOptionSplitted[$key]['source.']);
+                $sourceLayout = $this->stdWrap(
+                    $srcLayoutOptionSplitted[$key]['source'] ?? '',
+                    $srcLayoutOptionSplitted[$key]['source.'] ?? []
+                );
 
                 $sourceRenderConfiguration = [
                     'file' => $file,
 
                 $sourceRenderConfiguration = [
                     'file' => $file,
-                    'file.' => $conf['file.']
+                    'file.' => $conf['file.'] ?? null
                 ];
 
                 if (isset($sourceConfiguration['quality']) || isset($sourceConfiguration['quality.'])) {
                 ];
 
                 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.']);
                     }
                     if (isset($sourceConfiguration['quality.'])) {
                         $imageQuality = $this->stdWrap($sourceConfiguration['quality'], $sourceConfiguration['quality.']);
                     }
@@ -1313,15 +1186,24 @@ class ContentObjectRenderer
                 }
 
                 if (isset($sourceConfiguration['pixelDensity'])) {
                 }
 
                 if (isset($sourceConfiguration['pixelDensity'])) {
-                    $pixelDensity = (int)$this->stdWrap($sourceConfiguration['pixelDensity'], $sourceConfiguration['pixelDensity.']);
+                    $pixelDensity = (int)$this->stdWrap(
+                        $sourceConfiguration['pixelDensity'] ?? '',
+                        $sourceConfiguration['pixelDensity.'] ?? []
+                    );
                 } else {
                     $pixelDensity = 1;
                 }
                 } else {
                     $pixelDensity = 1;
                 }
-                $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH'];
+                $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
                 foreach ($dimensionKeys as $dimensionKey) {
                 foreach ($dimensionKeys as $dimensionKey) {
-                    $dimension = $this->stdWrap($sourceConfiguration[$dimensionKey], $sourceConfiguration[$dimensionKey . '.']);
+                    $dimension = $this->stdWrap(
+                        $sourceConfiguration[$dimensionKey] ?? '',
+                        $sourceConfiguration[$dimensionKey . '.'] ?? []
+                    );
                     if (!$dimension) {
                     if (!$dimension) {
-                        $dimension = $this->stdWrap($conf['file.'][$dimensionKey], $conf['file.'][$dimensionKey . '.']);
+                        $dimension = $this->stdWrap(
+                            $conf['file.'][$dimensionKey] ?? '',
+                            $conf['file.'][$dimensionKey . '.'] ?? []
+                        );
                     }
                     if ($dimension) {
                         if (strstr($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
                     }
                     if ($dimension) {
                         if (strstr($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
@@ -1330,6 +1212,12 @@ class ContentObjectRenderer
                             if ($dimensionParts[1]) {
                                 $dimension .= $dimensionParts[1];
                             }
                             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;
                         }
                         } else {
                             $dimension = (int)$dimension * $pixelDensity;
                         }
@@ -1349,19 +1237,18 @@ class ContentObjectRenderer
                     $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
                     $sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : '';
 
                     $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
                     $sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : '';
 
-                    $oneSourceCollection = $this->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
+                    $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
+                    $oneSourceCollection = $markerTemplateEngine->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;
                     }
 
                     $sourceCollection .= $oneSourceCollection;
@@ -1461,7 +1348,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'];
                 $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'];
@@ -1490,38 +1377,11 @@ 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()
-     */
-    public function fileResource($fName, $addParams = 'alt="" title=""')
-    {
-        $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.
      *
      * @param int $tstamp Unix timestamp (number of seconds since 1970)
      * 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.
      *
      * @param int $tstamp Unix timestamp (number of seconds since 1970)
-     * @return void
      * @see TypoScriptFrontendController::setSysLastChanged()
      */
     public function lastChanged($tstamp)
      * @see TypoScriptFrontendController::setSysLastChanged()
      */
     public function lastChanged($tstamp)
@@ -1546,11 +1406,12 @@ class ContentObjectRenderer
     {
         $wrapArr = explode('|', $wrap);
         if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
     {
         $wrapArr = explode('|', $wrap);
         if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
-            if ($uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid']) {
+            $uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid'] ?? null;
+            if ($uid) {
                 $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
             }
         }
                 $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
             }
         }
-        return trim($wrapArr[0]) . $content . trim($wrapArr[1]);
+        return trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
     }
 
     /**
     }
 
     /**
@@ -1602,24 +1463,22 @@ class ContentObjectRenderer
     public function getATagParams($conf, $addGlobal = 1)
     {
         $aTagParams = '';
     public function getATagParams($conf, $addGlobal = 1)
     {
         $aTagParams = '';
-        if ($conf['ATagParams.']) {
+        if ($conf['ATagParams.'] ?? false) {
             $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
             $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
-        } elseif ($conf['ATagParams']) {
+        } elseif ($conf['ATagParams'] ?? false) {
             $aTagParams = ' ' . $conf['ATagParams'];
         }
         if ($addGlobal) {
             $aTagParams = ' ' . trim($this->getTypoScriptFrontendController()->ATagParams . $aTagParams);
         }
         // Extend params
             $aTagParams = ' ' . $conf['ATagParams'];
         }
         if ($addGlobal) {
             $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);
         }
 
         $aTagParams = trim($aTagParams);
@@ -1641,8 +1500,8 @@ class ContentObjectRenderer
     public function extLinkATagParams($URL, $TYPE)
     {
         $out = '';
     public function extLinkATagParams($URL, $TYPE)
     {
         $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']);
+        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler'])) {
+            $extLinkATagParamsHandler = GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']);
             if (method_exists($extLinkATagParamsHandler, 'main')) {
                 $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
             }
             if (method_exists($extLinkATagParamsHandler, 'main')) {
                 $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
             }
@@ -1655,302 +1514,6 @@ class ContentObjectRenderer
      * HTML template processing functions
      *
      ***********************************************/
      * 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.
-     */
-    public function getSubpart($content, $marker)
-    {
-        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.
-     */
-    public function substituteSubpart($content, $marker, $subpartContent, $recursive = 1)
-    {
-        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.
-     */
-    public function substituteSubpartArray($content, array $subpartsContent)
-    {
-        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()
-     */
-    public function substituteMarker($content, $marker, $markContent)
-    {
-        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()
-     */
-    public function substituteMarkerArrayCached($content, array $markContentArray = null, array $subpartContentArray = null, array $wrappedSubpartContentArray = null)
-    {
-        $timeTracker = $this->getTimeTracker();
-        $timeTracker->push('substituteMarkerArrayCached');
-        // If not arrays then set them
-        if (is_null($markContentArray)) {
-            // Plain markers
-            $markContentArray = [];
-        }
-        if (is_null($subpartContentArray)) {
-            // Subparts being directly substituted
-            $subpartContentArray = [];
-        }
-        if (is_null($wrappedSubpartContentArray)) {
-            // Subparts being wrapped
-            $wrappedSubpartContentArray = [];
-        }
-        // Finding keys and check hash:
-        $sPkeys = array_keys($subpartContentArray);
-        $wPkeys = array_keys($wrappedSubpartContentArray);
-        $keysToReplace = array_merge(array_keys($markContentArray), $sPkeys, $wPkeys);
-        if (empty($keysToReplace)) {
-            $timeTracker->pull();
-            return $content;
-        }
-        asort($keysToReplace);
-        $storeKey = md5('substituteMarkerArrayCached_storeKey:' . serialize([$content, $keysToReplace]));
-        if ($this->substMarkerCache[$storeKey]) {
-            $storeArr = $this->substMarkerCache[$storeKey];
-            $timeTracker->setTSlogMessage('Cached', 0);
-        } else {
-            $storeArrDat = $this->getTypoScriptFrontendController()->sys_page->getHash($storeKey);
-            if (is_array($storeArrDat)) {
-                $storeArr = $storeArrDat;
-                // Setting cache:
-                $this->substMarkerCache[$storeKey] = $storeArr;
-                $timeTracker->setTSlogMessage('Cached from DB', 0);
-            } else {
-                // Finding subparts and substituting them with the subpart as a marker
-                foreach ($sPkeys as $sPK) {
-                    $content = $this->substituteSubpart($content, $sPK, $sPK);
-                }
-                // Finding subparts and wrapping them with markers
-                foreach ($wPkeys as $wPK) {
-                    $content = $this->substituteSubpart($content, $wPK, [
-                        $wPK,
-                        $wPK
-                    ]);
-                }
-
-                $storeArr = [];
-                // search all markers in the content
-                $result = preg_match_all('/###([^#](?:[^#]*+|#{1,2}[^#])+)###/', $content, $markersInContent);
-                if ($result !== false && !empty($markersInContent[1])) {
-                    $keysToReplaceFlipped = array_flip($keysToReplace);
-                    $regexKeys = [];
-                    $wrappedKeys = [];
-                    // Traverse keys and quote them for reg ex.
-                    foreach ($markersInContent[1] as $key) {
-                        if (isset($keysToReplaceFlipped['###' . $key . '###'])) {
-                            $regexKeys[] = preg_quote($key, '/');
-                            $wrappedKeys[] = '###' . $key . '###';
-                        }
-                    }
-                    $regex = '/###(?:' . implode('|', $regexKeys) . ')###/';
-                    $storeArr['c'] = preg_split($regex, $content); // contains all content parts around markers
-                    $storeArr['k'] = $wrappedKeys; // contains all markers incl. ###
-                    // Setting cache:
-                    $this->substMarkerCache[$storeKey] = $storeArr;
-                    // Storing the cached data:
-                    $this->getTypoScriptFrontendController()->sys_page->storeHash($storeKey, $storeArr, 'substMarkArrayCached');
-                }
-                $timeTracker->setTSlogMessage('Parsing', 0);
-            }
-        }
-        if (!empty($storeArr['k']) && is_array($storeArr['k'])) {
-            // Substitution/Merging:
-            // Merging content types together, resetting
-            $valueArr = array_merge($markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
-            $wSCA_reg = [];
-            $content = '';
-            // Traversing the keyList array and merging the static and dynamic content
-            foreach ($storeArr['k'] as $n => $keyN) {
-                // add content before marker
-                $content .= $storeArr['c'][$n];
-                if (!is_array($valueArr[$keyN])) {
-                    // fetch marker replacement from $markContentArray or $subpartContentArray
-                    $content .= $valueArr[$keyN];
-                } else {
-                    if (!isset($wSCA_reg[$keyN])) {
-                        $wSCA_reg[$keyN] = 0;
-                    }
-                    // fetch marker replacement from $wrappedSubpartContentArray
-                    $content .= $valueArr[$keyN][$wSCA_reg[$keyN] % 2];
-                    $wSCA_reg[$keyN]++;
-                }
-            }
-            // add remaining content
-            $content .= $storeArr['c'][count($storeArr['k'])];
-        }
-        $timeTracker->pull();
-        return $content;
-    }
-
-    /**
-     * 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()
-     */
-    public function substituteMarkerArray($content, array $markContentArray, $wrap = '', $uppercase = false, $deleteUnused = false)
-    {
-        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()
-     */
-    public function substituteMarkerInObject(&$tree, array $markContentArray)
-    {
-        if (is_array($tree)) {
-            foreach ($tree as $key => $value) {
-                $this->substituteMarkerInObject($tree[$key], $markContentArray);
-            }
-        } else {
-            $tree = $this->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
-     */
-    public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = false, $deleteUnused = false)
-    {
-        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
-     */
-    public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = true, $prefix = 'FIELD_', $HSC = false)
-    {
-        $tsfe = $this->getTypoScriptFrontendController();
-        if ($fieldList) {
-            $fArr = GeneralUtility::trimExplode(',', $fieldList, true);
-            foreach ($fArr as $field) {
-                $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($row[$field], !empty($tsfe->xhtmlDoctype)) : $row[$field];
-            }
-        } else {
-            if (is_array($row)) {
-                foreach ($row as $field => $value) {
-                    if (!MathUtility::canBeInterpretedAsInteger($field)) {
-                        if ($HSC) {
-                            $value = htmlspecialchars($value);
-                        }
-                        $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($value, !empty($tsfe->xhtmlDoctype)) : $value;
-                    }
-                }
-            }
-        }
-        return $markContentArray;
-    }
 
     /**
      * Sets the current file object during iterations over files.
 
     /**
      * Sets the current file object during iterations over files.
@@ -2005,7 +1568,7 @@ class ContentObjectRenderer
         }
 
         // Cache handling
         }
 
         // Cache handling
-        if (is_array($conf['cache.'])) {
+        if (isset($conf['cache.']) && is_array($conf['cache.'])) {
             $conf['cache.']['key'] = $this->stdWrap($conf['cache.']['key'], $conf['cache.']['key.']);
             $conf['cache.']['tags'] = $this->stdWrap($conf['cache.']['tags'], $conf['cache.']['tags.']);
             $conf['cache.']['lifetime'] = $this->stdWrap($conf['cache.']['lifetime'], $conf['cache.']['lifetime.']);
             $conf['cache.']['key'] = $this->stdWrap($conf['cache.']['key'], $conf['cache.']['key.']);
             $conf['cache.']['tags'] = $this->stdWrap($conf['cache.']['tags'], $conf['cache.']['tags.']);
             $conf['cache.']['lifetime'] = $this->stdWrap($conf['cache.']['lifetime'], $conf['cache.']['lifetime.']);
@@ -2025,29 +1588,27 @@ class ContentObjectRenderer
         // execute each function in the predefined order
         foreach ($sortedConf as $stdWrapName) {
             // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
         // execute each function in the predefined order
         foreach ($sortedConf as $stdWrapName) {
             // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
-            if (!$isExecuted[$stdWrapName] && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
+            if ((!isset($isExecuted[$stdWrapName]) || !$isExecuted[$stdWrapName]) && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
                 $functionName = rtrim($stdWrapName, '.');
                 $functionProperties = $functionName . '.';
                 $functionName = rtrim($stdWrapName, '.');
                 $functionProperties = $functionName . '.';
-                $functionType = $this->stdWrapOrder[$functionName];
+                $functionType = $this->stdWrapOrder[$functionName] ?? null;
                 // If there is any code on the next level, check if it contains "official" stdWrap functions
                 // if yes, execute them first - will make each function stdWrap aware
                 // so additional stdWrap calls within the functions can be removed, since the result will be the same
                 if (!empty($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
                     if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
                 // If there is any code on the next level, check if it contains "official" stdWrap functions
                 // if yes, execute them first - will make each function stdWrap aware
                 // so additional stdWrap calls within the functions can be removed, since the result will be the same
                 if (!empty($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
                     if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
-                        $conf[$functionName] = $this->stdWrap($conf[$functionName], $conf[$functionProperties]);
+                        $conf[$functionName] = $this->stdWrap($conf[$functionName] ?? '', $conf[$functionProperties] ?? []);
                     }
                 }
                 // Check if key is still containing something, since it might have been changed by next level stdWrap before
                     }
                 }
                 // Check if key is still containing something, since it might have been changed by next level stdWrap before
-                if ((isset($conf[$functionName]) || $conf[$functionProperties]) && ($functionType !== 'boolean' || $conf[$functionName])) {
+                if ((isset($conf[$functionName]) || $conf[$functionProperties])
+                    && ($functionType !== 'boolean' || $conf[$functionName])
+                ) {
                     // Get just that part of $conf that is needed for the particular function
                     $singleConf = [
                     // Get just that part of $conf that is needed for the particular function
                     $singleConf = [
-                        $functionName => $conf[$functionName],
-                        $functionProperties => $conf[$functionProperties]
+                        $functionName => $conf[$functionName] ?? null,
+                        $functionProperties => $conf[$functionProperties] ?? null
                     ];
                     ];
-                    // 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;
                     // Hand over the whole $conf array to the stdWrapHookObjects
                     if ($functionType === 'hook') {
                         $singleConf = $conf;
@@ -2169,7 +1730,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_setCurrent($content = '', $conf = [])
     {
      */
     public function stdWrap_setCurrent($content = '', $conf = [])
     {
-        $this->data[$this->currentValKey] = $conf['setCurrent'];
+        $this->data[$this->currentValKey] = $conf['setCurrent'] ?? null;
         return $content;
     }
 
         return $content;
     }
 
@@ -2184,7 +1745,12 @@ class ContentObjectRenderer
     public function stdWrap_lang($content = '', $conf = [])
     {
         $tsfe = $this->getTypoScriptFrontendController();
     public function stdWrap_lang($content = '', $conf = [])
     {
         $tsfe = $this->getTypoScriptFrontendController();
-        if (isset($conf['lang.']) && $tsfe->config['config']['language'] && isset($conf['lang.'][$tsfe->config['config']['language']])) {
+        if (
+            isset($conf['lang.'])
+            && isset($tsfe->config['config']['language'])
+            && $tsfe->config['config']['language']
+            && isset($conf['lang.'][$tsfe->config['config']['language']])
+        ) {
             $content = $conf['lang.'][$tsfe->config['config']['language']];
         }
         return $content;
             $content = $conf['lang.'][$tsfe->config['config']['language']];
         }
         return $content;
@@ -2244,7 +1810,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_cObject($content = '', $conf = [])
     {
      */
     public function stdWrap_cObject($content = '', $conf = [])
     {
-        return $this->cObjGetSingle($conf['cObject'], $conf['cObject.'], '/stdWrap/.cObject');
+        return $this->cObjGetSingle($conf['cObject'] ?? '', $conf['cObject.'] ?? [], '/stdWrap/.cObject');
     }
 
     /**
     }
 
     /**
@@ -2315,7 +1881,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_override($content = '', $conf = [])
     {
      */
     public function stdWrap_override($content = '', $conf = [])
     {
-        if (trim($conf['override'])) {
+        if (trim($conf['override'] ?? false)) {
             $content = $conf['override'];
         }
         return $content;
             $content = $conf['override'];
         }
         return $content;
@@ -2332,20 +1898,20 @@ class ContentObjectRenderer
      */
     public function stdWrap_preIfEmptyListNum($content = '', $conf = [])
     {
      */
     public function stdWrap_preIfEmptyListNum($content = '', $conf = [])
     {
-        return $this->listNum($content, $conf['preIfEmptyListNum'], $conf['preIfEmptyListNum.']['splitChar']);
+        return $this->listNum($content, $conf['preIfEmptyListNum'] ?? null, $conf['preIfEmptyListNum.']['splitChar'] ?? null);
     }
 
     /**
      * ifNull
      * Will set content to a replacement value in case the value of content is NULL
      *
     }
 
     /**
      * 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 = [])
     {
      * @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'];
     }
 
     /**
     }
 
     /**
@@ -2394,7 +1960,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_listNum($content = '', $conf = [])
     {
      */
     public function stdWrap_listNum($content = '', $conf = [])
     {
-        return $this->listNum($content, $conf['listNum'], $conf['listNum.']['splitChar']);
+        return $this->listNum($content, $conf['listNum'] ?? null, $conf['listNum.']['splitChar'] ?? null);
     }
 
     /**
     }
 
     /**
@@ -2523,7 +2089,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_fieldRequired($content = '', $conf = [])
     {
      */
     public function stdWrap_fieldRequired($content = '', $conf = [])
     {
-        if (!trim($this->data[$conf['fieldRequired']])) {
+        if (!trim($this->data[$conf['fieldRequired'] ?? null] ?? '')) {
             $content = '';
             $this->stopRendering[$this->stdWrapRecursionLevel] = true;
         }
             $content = '';
             $this->stopRendering[$this->stdWrapRecursionLevel] = true;
         }
@@ -2531,23 +2097,22 @@ class ContentObjectRenderer
     }
 
     /**
     }
 
     /**
-     * csConv
-     * Will convert the current chracter set of the content to the one given in csConv
+     * stdWrap csConv: Converts the input to UTF-8
      *
      *
-     * @param string $content Input value undergoing processing in this function.
+     * The character set of the input must be specified. Returns the input if
+     * matters go wrong, for example if an invalid character set is given.
+     *
+     * @param string $content The string to convert.
      * @param array $conf stdWrap properties for csConv.
      * @param array $conf stdWrap properties for csConv.
-     * @return string The processed input value
+     * @return string The processed input.
      */
     public function stdWrap_csConv($content = '', $conf = [])
     {
         if (!empty($conf['csConv'])) {
      */
     public function stdWrap_csConv($content = '', $conf = [])
     {
         if (!empty($conf['csConv'])) {
-            /** @var CharsetConverter $charsetConverter */
-            $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
-            $output = $charsetConverter->conv($content, $charsetConverter->parse_charset($conf['csConv']), 'utf-8');
-            return $output ?: $content;
-        } else {
-            return $content;
+            $output = mb_convert_encoding($content, 'utf-8', trim(strtolower($conf['csConv'])));
+            return $output !== false && $output !== '' ? $output : $content;
         }
         }
+        return $content;
     }
 
     /**
     }
 
     /**
@@ -2575,7 +2140,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_HTMLparser($content = '', $conf = [])
     {
      */
     public function stdWrap_HTMLparser($content = '', $conf = [])
     {
-        if (is_array($conf['HTMLparser.'])) {
+        if (isset($conf['HTMLparser.']) && is_array($conf['HTMLparser.'])) {
             $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
         }
         return $content;
             $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
         }
         return $content;
@@ -2620,7 +2185,7 @@ class ContentObjectRenderer
     public function stdWrap_prioriCalc($content = '', $conf = [])
     {
         $content = MathUtility::calculateWithParentheses($content);
     public function stdWrap_prioriCalc($content = '', $conf = [])
     {
         $content = MathUtility::calculateWithParentheses($content);
-        if ($conf['prioriCalc'] === 'intval') {
+        if (!empty($conf['prioriCalc']) && $conf['prioriCalc'] === 'intval') {
             $content = (int)$content;
         }
         return $content;
             $content = (int)$content;
         }
         return $content;
@@ -2695,7 +2260,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_numberFormat($content = '', $conf = [])
     {
      */
     public function stdWrap_numberFormat($content = '', $conf = [])
     {
-        return $this->numberFormat($content, $conf['numberFormat.']);
+        return $this->numberFormat($content, $conf['numberFormat.'] ?? []);
     }
 
     /**
     }
 
     /**
@@ -2723,7 +2288,7 @@ class ContentObjectRenderer
     {
         // Check for zero length string to mimic default case of date/gmdate.
         $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
     {
         // Check for zero length string to mimic default case of date/gmdate.
         $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
-        $content = $conf['date.']['GMT'] ? gmdate($conf['date'], $content) : date($conf['date'], $content);
+        $content = !empty($conf['date.']['GMT']) ? gmdate($conf['date'] ?? null, $content) : date($conf['date'] ?? null, $content);
         return $content;
     }
 
         return $content;
     }
 
@@ -2740,11 +2305,11 @@ class ContentObjectRenderer
     {
         // Check for zero length string to mimic default case of strtime/gmstrftime
         $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
     {
         // Check for zero length string to mimic default case of strtime/gmstrftime
         $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
-        $content = $conf['strftime.']['GMT'] ? gmstrftime($conf['strftime'], $content) : strftime($conf['strftime'], $content);
+        $content = (isset($conf['strftime.']['GMT']) && $conf['strftime.']['GMT'])
+            ? gmstrftime($conf['strftime'] ?? null, $content)
+            : strftime($conf['strftime'] ?? null, $content);
         if (!empty($conf['strftime.']['charset'])) {
         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;
             return $output ?: $content;
         }
         return $content;
@@ -2776,7 +2341,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_age($content = '', $conf = [])
     {
      */
     public function stdWrap_age($content = '', $conf = [])
     {
-        return $this->calcAge((int)$GLOBALS['EXEC_TIME'] - (int)$content, $conf['age']);
+        return $this->calcAge((int)($GLOBALS['EXEC_TIME'] ?? 0) - (int)$content, $conf['age'] ?? null);
     }
 
     /**
     }
 
     /**
@@ -2820,19 +2385,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
      *
      * cropHTML
      * Crops content to a given size while leaving HTML tags untouched
      *
@@ -2842,7 +2394,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_cropHTML($content = '', $conf = [])
     {
      */
     public function stdWrap_cropHTML($content = '', $conf = [])
     {
-        return $this->cropHTML($content, $conf['cropHTML']);
+        return $this->cropHTML($content, $conf['cropHTML'] ?? '');
     }
 
     /**
     }
 
     /**
@@ -2924,7 +2476,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_doubleBrTag($content = '', $conf = [])
     {
      */
     public function stdWrap_doubleBrTag($content = '', $conf = [])
     {
-        return preg_replace('/\R{1,2}[\t\x20]*\R{1,2}/', $conf['doubleBrTag'], $content);
+        return preg_replace('/\R{1,2}[\t\x20]*\R{1,2}/', $conf['doubleBrTag'] ?? null, $content);
     }
 
     /**
     }
 
     /**
@@ -2933,7 +2485,6 @@ class ContentObjectRenderer
      * according to the doctype
      *
      * @param string $content Input value undergoing processing in this function.
      * according to the doctype
      *
      * @param string $content Input value undergoing processing in this function.
-     * @param array $conf stdWrap properties for br.
      * @return string The processed input value
      */
     public function stdWrap_br($content = '')
      * @return string The processed input value
      */
     public function stdWrap_br($content = '')
@@ -2951,7 +2502,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_brTag($content = '', $conf = [])
     {
      */
     public function stdWrap_brTag($content = '', $conf = [])
     {
-        return str_replace(LF, $conf['brTag'], $content);
+        return str_replace(LF, $conf['brTag'] ?? null, $content);
     }
 
     /**
     }
 
     /**
@@ -2991,7 +2542,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_innerWrap($content = '', $conf = [])
     {
      */
     public function stdWrap_innerWrap($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['innerWrap']);
+        return $this->wrap($content, $conf['innerWrap'] ?? null);
     }
 
     /**
     }
 
     /**
@@ -3005,23 +2556,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_innerWrap2($content = '', $conf = [])
     {
      */
     public function stdWrap_innerWrap2($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['innerWrap2']);
-    }
-
-    /**
-     * 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']);
+        return $this->wrap($content, $conf['innerWrap2'] ?? null);
     }
 
     /**
     }
 
     /**
@@ -3034,7 +2569,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_addParams($content = '', $conf = [])
     {
      */
     public function stdWrap_addParams($content = '', $conf = [])
     {
-        return $this->addParams($content, $conf['addParams.']);
+        return $this->addParams($content, $conf['addParams.'] ?? []);
     }
 
     /**
     }
 
     /**
@@ -3048,7 +2583,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_filelink($content = '', $conf = [])
     {
      */
     public function stdWrap_filelink($content = '', $conf = [])
     {
-        return $this->filelink($content, $conf['filelink.']);
+        return $this->filelink($content, $conf['filelink.'] ?? []);
     }
 
     /**
     }
 
     /**
@@ -3088,7 +2623,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_wrapAlign($content = '', $conf = [])
     {
      */
     public function stdWrap_wrapAlign($content = '', $conf = [])
     {
-        $wrapAlign = trim($conf['wrapAlign']);
+        $wrapAlign = trim($conf['wrapAlign'] ?? '');
         if ($wrapAlign) {
             $content = $this->wrap($content, '<div style="text-align:' . htmlspecialchars($wrapAlign) . ';">|</div>');
         }
         if ($wrapAlign) {
             $content = $this->wrap($content, '<div style="text-align:' . htmlspecialchars($wrapAlign) . ';">|</div>');
         }
@@ -3111,65 +2646,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
      * 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
@@ -3183,7 +2659,11 @@ class ContentObjectRenderer
      */
     public function stdWrap_wrap($content = '', $conf = [])
     {
      */
     public function stdWrap_wrap($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['wrap'], $conf['wrap.']['splitChar'] ? $conf['wrap.']['splitChar'] : '|');
+        return $this->wrap(
+            $content,
+            $conf['wrap'] ?? null,
+            $conf['wrap.']['splitChar'] ?? '|'
+        );
     }
 
     /**
     }
 
     /**
@@ -3198,8 +2678,8 @@ class ContentObjectRenderer
     public function stdWrap_noTrimWrap($content = '', $conf = [])
     {
         $splitChar = isset($conf['noTrimWrap.']['splitChar.'])
     public function stdWrap_noTrimWrap($content = '', $conf = [])
     {
         $splitChar = isset($conf['noTrimWrap.']['splitChar.'])
-            ? $this->stdWrap($conf['noTrimWrap.']['splitChar'], $conf['noTrimWrap.']['splitChar.'])
-            : $conf['noTrimWrap.']['splitChar'];
+            ? $this->stdWrap($conf['noTrimWrap.']['splitChar'] ?? '', $conf['noTrimWrap.']['splitChar.'])
+            : $conf['noTrimWrap.']['splitChar'] ?? '';
         if ($splitChar === null || $splitChar === '') {
             $splitChar = '|';
         }
         if ($splitChar === null || $splitChar === '') {
             $splitChar = '|';
         }
@@ -3222,7 +2702,11 @@ class ContentObjectRenderer
      */
     public function stdWrap_wrap2($content = '', $conf = [])
     {
      */
     public function stdWrap_wrap2($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['wrap2'], $conf['wrap2.']['splitChar'] ? $conf['wrap2.']['splitChar'] : '|');
+        return $this->wrap(
+            $content,
+            $conf['wrap2'] ?? null,
+            $conf['wrap2.']['splitChar'] ?? '|'
+        );
     }
 
     /**
     }
 
     /**
@@ -3276,7 +2760,11 @@ class ContentObjectRenderer
      */
     public function stdWrap_wrap3($content = '', $conf = [])
     {
      */
     public function stdWrap_wrap3($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['wrap3'], $conf['wrap3.']['splitChar'] ? $conf['wrap3.']['splitChar'] : '|');
+        return $this->wrap(
+            $content,
+            $conf['wrap3'] ?? null,
+            $conf['wrap3.']['splitChar'] ?? '|'
+        );
     }
 
     /**
     }
 
     /**
@@ -3291,7 +2779,7 @@ class ContentObjectRenderer
     {
         $sortedKeysArray = ArrayUtility::filterAndSortByNumericKeys($conf['orderedStdWrap.'], true);
         foreach ($sortedKeysArray as $key) {
     {
         $sortedKeysArray = ArrayUtility::filterAndSortByNumericKeys($conf['orderedStdWrap.'], true);
         foreach ($sortedKeysArray as $key) {
-            $content = $this->stdWrap($content, $conf['orderedStdWrap.'][$key . '.']);
+            $content = $this->stdWrap($content, $conf['orderedStdWrap.'][$key . '.'] ?? null);
         }
         return $content;
     }
         }
         return $content;
     }
@@ -3306,7 +2794,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_outerWrap($content = '', $conf = [])
     {
      */
     public function stdWrap_outerWrap($content = '', $conf = [])
     {
-        return $this->wrap($content, $conf['outerWrap']);
+        return $this->wrap($content, $conf['outerWrap'] ?? null);
     }
 
     /**
     }
 
     /**
@@ -3367,7 +2855,10 @@ class ContentObjectRenderer
      */
     public function stdWrap_prefixComment($content = '', $conf = [])
     {
      */
     public function stdWrap_prefixComment($content = '', $conf = [])
     {
-        if (!$this->getTypoScriptFrontendController()->config['config']['disablePrefixComment'] && !empty($conf['prefixComment'])) {
+        if (
+            (!isset($this->getTypoScriptFrontendController()->config['config']['disablePrefixComment']) || !$this->getTypoScriptFrontendController()->config['config']['disablePrefixComment'])
+            && !empty($conf['prefixComment'])
+        ) {
             $content = $this->prefixComment($conf['prefixComment'], [], $content);
         }
         return $content;
             $content = $this->prefixComment($conf['prefixComment'], [], $content);
         }
         return $content;
@@ -3383,8 +2874,8 @@ class ContentObjectRenderer
      */
     public function stdWrap_editIcons($content = '', $conf = [])
     {
      */
     public function stdWrap_editIcons($content = '', $conf = [])
     {
-        if ($this->getTypoScriptFrontendController()->beUserLogin && $conf['editIcons']) {
-            if (!is_array($conf['editIcons.'])) {
+        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $conf['editIcons']) {
+            if (!isset($conf['editIcons.']) || !is_array($conf['editIcons.'])) {
                 $conf['editIcons.'] = [];
             }
             $content = $this->editIcons($content, $conf['editIcons'], $conf['editIcons.']);
                 $conf['editIcons.'] = [];
             }
             $content = $this->editIcons($content, $conf['editIcons'], $conf['editIcons.']);
@@ -3402,7 +2893,7 @@ class ContentObjectRenderer
      */
     public function stdWrap_editPanel($content = '', $conf = [])
     {
      */
     public function stdWrap_editPanel($content = '', $conf = [])
     {
-        if ($this->getTypoScriptFrontendController()->beUserLogin) {
+        if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn()) {
             $content = $this->editPanel($content, $conf['editPanel.']);
         }
         return $content;
             $content = $this->editPanel($content, $conf['editPanel.']);
         }
         return $content;
@@ -3424,20 +2915,18 @@ class ContentObjectRenderer
         if (empty($key)) {
             return $content;
         }
         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.']);
         $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;
         }
         $cacheFrontend->set($key, $content, $tags, $lifetime);
         return $content;
@@ -3586,7 +3075,9 @@ class ContentObjectRenderer
             }
         }
         if ($flag) {
             }
         }
         if ($flag) {
-            $value = isset($conf['value.']) ? trim($this->stdWrap($conf['value'], $conf['value.'])) : trim($conf['value']);
+            $value = isset($conf['value.'])
+                ? trim($this->stdWrap($conf['value'] ?? '', $conf['value.']))
+                : (trim($conf['value'] ?? ''));
             if (isset($conf['isGreaterThan']) || isset($conf['isGreaterThan.'])) {
                 $number = isset($conf['isGreaterThan.']) ? trim($this->stdWrap($conf['isGreaterThan'], $conf['isGreaterThan.'])) : trim($conf['isGreaterThan']);
                 if ($number <= $value) {
             if (isset($conf['isGreaterThan']) || isset($conf['isGreaterThan.'])) {
                 $number = isset($conf['isGreaterThan.']) ? trim($this->stdWrap($conf['isGreaterThan'], $conf['isGreaterThan.'])) : trim($conf['isGreaterThan']);
                 if ($number <= $value) {
@@ -3612,7 +3103,7 @@ class ContentObjectRenderer
                 }
             }
         }
                 }
             }
         }
-        if ($conf['negate']) {
+        if ($conf['negate'] ?? false) {
             $flag = !$flag;
         }
         return $flag;
             $flag = !$flag;
         }
         return $flag;
@@ -3633,15 +3124,28 @@ class ContentObjectRenderer
         if ($data === '') {
             return '';
         }
         if ($data === '') {
             return '';
         }
-        $data_arr = explode('|', $data);
+        list($possiblePath, $ext_list, $sorting, $reverse, $useFullPath) = GeneralUtility::trimExplode('|', $data);
         // read directory:
         // MUST exist!
         $path = '';
         // 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 '';
         }
         if (!$path) {
             return '';
@@ -3650,14 +3154,13 @@ class ContentObjectRenderer
             'files' => [],
             'sorting' => []
         ];
             '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)) {
             $count = 0;
             while ($entry = $d->read()) {
         // Read dir:
         $d = @dir($path);
         if (is_object($d)) {
             $count = 0;
             while ($entry = $d->read()) {
-                if ($entry != '.' && $entry != '..') {
+                if ($entry !== '.' && $entry !== '..') {
                     // Because of odd PHP-error where <br />-tag is sometimes placed after a filename!!
                     $wholePath = $path . '/' . $entry;
                     if (file_exists($wholePath) && filetype($wholePath) === 'file') {
                     // Because of odd PHP-error where <br />-tag is sometimes placed after a filename!!
                     $wholePath = $path . '/' . $entry;
                     if (file_exists($wholePath) && filetype($wholePath) === 'file') {
@@ -3692,7 +3195,7 @@ class ContentObjectRenderer
         }
         // Sort if required
         if (!empty($items['sorting'])) {
         }
         // 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']);
                 asort($items['sorting']);
             } else {
                 arsort($items['sorting']);
@@ -3701,10 +3204,9 @@ class ContentObjectRenderer
         if (!empty($items['files'])) {
             // Make list
             reset($items['sorting']);
         if (!empty($items['files'])) {
             // Make list
             reset($items['sorting']);
-            $fullPath = trim($data_arr[4]);
             $list_arr = [];
             foreach ($items['sorting'] as $key => $v) {
             $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);
         }
             }
             return implode(',', $list_arr);
         }
@@ -3712,27 +3214,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.
      * 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.
@@ -3763,8 +3244,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
      *
      * @param string $str Input value
      * @return string Processed input value
@@ -3781,6 +3267,12 @@ class ContentObjectRenderer
                 $len = strcspn(substr($str, $pointer), '{');
                 $newVal .= substr($str, $pointer, $len);
                 $inside = true;
                 $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);
             } else {
                 $len = strcspn(substr($str, $pointer), '}') + 1;
                 $newVal .= $this->getData(substr($str, $pointer + 1, $len - 2), $this->data);
@@ -3810,10 +3302,10 @@ class ContentObjectRenderer
         $indent = (int)$parts[0];
         $comment = htmlspecialchars($this->insertData($parts[1]));
         $output = LF
         $indent = (int)$parts[0];
         $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, TAB) . '<!-- ' . $comment . ' [end] -->' . LF
-            . str_pad('', ($indent + 1), TAB);
+            . str_pad('', $indent, "\t") . '<!-- ' . $comment . ' [begin] -->' . LF
+            . str_pad('', $indent + 1, "\t") . $content . LF
+            . str_pad('', $indent, "\t") . '<!-- ' . $comment . ' [end] -->' . LF
+            . str_pad('', $indent + 1, "\t");
         return $output;
     }
 
         return $output;
     }
 
@@ -3828,14 +3320,11 @@ class ContentObjectRenderer
      */
     public function substring($content, $options)
     {
      */
     public function substring($content, $options)
     {
-        /** @var CharsetConverter $charsetConverter */
-        $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
         $options = GeneralUtility::intExplode(',', $options . ',');
         if ($options[1]) {
         $options = GeneralUtility::intExplode(',', $options . ',');
         if ($options[1]) {
-            return $charsetConverter->substr('utf-8', $content, $options[0], $options[1]);
-        } else {
-            return $charsetConverter->substr('utf-8', $content, $options[0]);
+            return mb_substr($content, $options[0], $options[1], 'utf-8');
         }
         }
+        return mb_substr($content, $options[0], null, 'utf-8');
     }
 
     /**
     }
 
     /**
@@ -3851,21 +3340,19 @@ class ContentObjectRenderer
     {
         $options = explode('|', $options);
         $chars = (int)$options[0];
     {
         $options = explode('|', $options);
         $chars = (int)$options[0];
-        $afterstring = trim($options[1]);
-        $crop2space = trim($options[2]);
+        $afterstring = trim($options[1] ?? '');
+        $crop2space = trim($options[2] ?? '');
         if ($chars) {
         if ($chars) {
-            /** @var CharsetConverter $charsetConverter */
-            $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
-            if ($charsetConverter->strlen('utf-8', $content) > abs($chars)) {
+            if (mb_strlen($content, 'utf-8') > abs($chars)) {
                 $truncatePosition = false;
                 if ($chars < 0) {
                 $truncatePosition = false;
                 if ($chars < 0) {
-                    $content = $charsetConverter->substr('utf-8', $content, $chars);
+                    $content = mb_substr($content, $chars, null, 'utf-8');
                     if ($crop2space) {
                         $truncatePosition = strpos($content, ' ');
                     }
                     $content = $truncatePosition ? $afterstring . substr($content, $truncatePosition) : $afterstring . $content;
                 } else {
                     if ($crop2space) {
                         $truncatePosition = strpos($content, ' ');
                     }
                     $content = $truncatePosition ? $afterstring . substr($content, $truncatePosition) : $afterstring . $content;
                 } else {
-                    $content = $charsetConverter->substr('utf-8', $content, 0, $chars);
+                    $content = mb_substr($content, 0, $chars, 'utf-8');
                     if ($crop2space) {
                         $truncatePosition = strrpos($content, ' ');
                     }
                     if ($crop2space) {
                         $truncatePosition = strrpos($content, ' ');
                     }
@@ -3894,8 +3381,8 @@ class ContentObjectRenderer
         $options = explode('|', $options);
         $chars = (int)$options[0];
         $absChars = abs($chars);
         $options = explode('|', $options);
         $chars = (int)$options[0];
         $absChars = abs($chars);
-        $replacementForEllipsis = trim($options[1]);
-        $crop2space = trim($options[2]) === '1';
+        $replacementForEllipsis = trim($options[1] ?? '');
+        $crop2space = trim($options[2] ?? '') === '1';
         // Split $content into an array(even items in the array are outside the tags, odd numbers are tag-blocks).
         $tags = 'a|abbr|address|area|article|aside|audio|b|bdi|bdo|blockquote|body|br|button|caption|cite|code|col|colgroup|data|datalist|dd|del|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|h1|h2|h3|h4|h5|h6|header|hr|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|main|map|mark|meter|nav|object|ol|optgroup|option|output|p|param|pre|progress|q|rb|rp|rt|rtc|ruby|s|samp|section|select|small|source|span|strong|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|tr|track|u|ul|ut|var|video|wbr';
         $tagsRegEx = '
         // Split $content into an array(even items in the array are outside the tags, odd numbers are tag-blocks).
         $tags = 'a|abbr|address|area|article|aside|audio|b|bdi|bdo|blockquote|body|br|button|caption|cite|code|col|colgroup|data|datalist|dd|del|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|h1|h2|h3|h4|h5|h6|header|hr|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|main|map|mark|meter|nav|object|ol|optgroup|option|output|p|param|pre|progress|q|rb|rp|rt|rtc|ruby|s|samp|section|select|small|source|span|strong|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|tr|track|u|ul|ut|var|video|wbr';
         $tagsRegEx = '
@@ -3947,13 +3434,11 @@ class ContentObjectRenderer
         $strLen = 0;
         // This is the offset of the content item which was cropped.
         $croppedOffset = null;
         $strLen = 0;
         // This is the offset of the content item which was cropped.
         $croppedOffset = null;
-        /** @var CharsetConverter $charsetConverter */
-        $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
         $countSplittedContent = count($splittedContent);
         for ($offset = 0; $offset < $countSplittedContent; $offset++) {
             if ($offset % 2 === 0) {
                 $tempContent = $splittedContent[$offset];
         $countSplittedContent = count($splittedContent);
         for ($offset = 0; $offset < $countSplittedContent; $offset++) {
             if ($offset % 2 === 0) {
                 $tempContent = $splittedContent[$offset];
-                $thisStrLen = $charsetConverter->strlen('utf-8', html_entity_decode($tempContent, ENT_COMPAT, 'UTF-8'));
+                $thisStrLen = mb_strlen(html_entity_decode($tempContent, ENT_COMPAT, 'UTF-8'), 'utf-8');
                 if ($strLen + $thisStrLen > $absChars) {
                     $croppedOffset = $offset;
                     $cropPosition = $absChars - $strLen;
                 if ($strLen + $thisStrLen > $absChars) {
                     $croppedOffset = $offset;
                     $cropPosition = $absChars - $strLen;
@@ -3977,9 +3462,8 @@ class ContentObjectRenderer
                     }
                     $splittedContent[$offset] = $tempContent;
                     break;
                     }
                     $splittedContent[$offset] = $tempContent;
                     break;
-                } else {
-                    $strLen += $thisStrLen;
                 }
                 }
+                $strLen += $thisStrLen;
             }
         }
         // Close cropped tags.
             }
         }
         // Close cropped tags.
@@ -3993,13 +3477,13 @@ class ContentObjectRenderer
                     continue;
                 }
                 preg_match($chars < 0 ? $closingTagRegEx : $openingTagRegEx, $splittedContent[$offset], $matches);
                     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);
                 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.
                         if ($tagName === $seekingTagName) {
                             // We found a matching tag.
                             // Add closing tag only if it occurs after the cropped content item.
@@ -4025,47 +3509,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 continously 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.
      * Implements the TypoScript function "addParams"
      *
      * @param string $content The string with the HTML tag.
@@ -4082,10 +3525,10 @@ class ContentObjectRenderer
         }
         $key = 1;
         $parts = explode('<', $content);
         }
         $key = 1;
         $parts = explode('<', $content);
-        if ((int)$conf['_offset']) {
+        if (isset($conf['_offset']) && (int)$conf['_offset']) {
             $key = (int)$conf['_offset'] < 0 ? count($parts) + (int)$conf['_offset'] : (int)$conf['_offset'];
         }
             $key = (int)$conf['_offset'] < 0 ? count($parts) + (int)$conf['_offset'] : (int)$conf['_offset'];
         }
-        $subparts = explode('>', $parts[$key]);
+        $subparts = explode('>', $parts[$key] ?? '');
         if (trim($subparts[0])) {
             // Get attributes and name
             $attribs = GeneralUtility::get_tag_attributes('<' . $subparts[0] . '>');
         if (trim($subparts[0])) {
             // Get attributes and name
             $attribs = GeneralUtility::get_tag_attributes('<' . $subparts[0] . '>');
@@ -4122,17 +3565,19 @@ class ContentObjectRenderer
      */
     public function filelink($theValue, $conf)
     {
      */
     public function filelink($theValue, $conf)
     {
-        $conf['path'] = isset($conf['path.']) ? $this->stdWrap($conf['path'], $conf['path.']) : $conf['path'];
+        $conf['path'] = isset($conf['path.'])
+            ? $this->stdWrap($conf['path'] ?? '', $conf['path.'])
+            : ($conf['path'] ?? '');
         $theFile = trim($conf['path']) . $theValue;
         if (!@is_file($theFile)) {
             return '';
         }
         $theFileEnc = str_replace('%2F', '/', rawurlencode($theFile));
         $theFile = trim($conf['path']) . $theValue;
         if (!@is_file($theFile)) {
             return '';
         }
         $theFileEnc = str_replace('%2F', '/', rawurlencode($theFile));
-        $title = $conf['title'];
+        $title = $conf['title'] ?? '';
         if (isset($conf['title.'])) {
             $title = $this->stdWrap($title, $conf['title.']);
         }
         if (isset($conf['title.'])) {
             $title = $this->stdWrap($title, $conf['title.']);
         }
-        $target = $conf['target'];
+        $target = $conf['target'] ?? '';
         if (isset($conf['target.'])) {
             $target = $this->stdWrap($target, $conf['target.']);
         }
         if (isset($conf['target.'])) {
             $target = $this->stdWrap($target, $conf['target.']);
         }
@@ -4156,20 +3601,21 @@ class ContentObjectRenderer
         $theSize = filesize($theFile);
         $fI = GeneralUtility::split_fileref($theFile);
         $icon = '';
         $theSize = filesize($theFile);
         $fI = GeneralUtility::split_fileref($theFile);
         $icon = '';
-        if ($conf['icon']) {
+        if ($conf['icon'] ?? false) {
             $conf['icon.']['path'] = isset($conf['icon.']['path.'])
                 ? $this->stdWrap($conf['icon.']['path'], $conf['icon.']['path.'])
                 : $conf['icon.']['path'];
             $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']
                 ? $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';
             $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));
             // 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));
@@ -4177,7 +3623,8 @@ class ContentObjectRenderer
                 if ($conf['iconCObject']) {
                     $icon = $this->cObjGetSingle($conf['iconCObject'], $conf['iconCObject.'], 'iconCObject');
                 } else {
                 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
                     $sizeParts = [64, 64];
                     if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails']) {
                         // using the File Abstraction Layer to generate a preview image
@@ -4210,8 +3657,8 @@ class ContentObjectRenderer
                         $urlPrefix = $tsfe->absRefPrefix;
                     }
                     $icon = '<img src="' . htmlspecialchars($urlPrefix . $icon) . '"' .
                         $urlPrefix = $tsfe->absRefPrefix;
                     }
                     $icon = '<img src="' . htmlspecialchars($urlPrefix . $icon) . '"' .
-                            ' width="' . (int)$sizeParts[0] . '" height="' . (int)$sizeParts[1] . '" ' .
-                            $this->getBorderAttr(' border="0"') . '' . $this->getAltParam($conf) . ' />';
+                        ' width="' . (int)$sizeParts[0] . '" height="' . (int)$sizeParts[1] . '" ' .
+                        $this->getBorderAttr(' border="0"') . '' . $this->getAltParam($conf) . ' />';
                 }
             } else {
                 $conf['icon.']['widthAttribute'] = isset($conf['icon.']['widthAttribute.'])
                 }
             } else {
                 $conf['icon.']['widthAttribute'] = isset($conf['icon.']['widthAttribute.'])
@@ -4231,19 +3678,21 @@ class ContentObjectRenderer
             $icon = isset($conf['icon.']) ? $this->stdWrap($icon, $conf['icon.']) : $icon;
         }
         $size = '';
             $icon = isset($conf['icon.']) ? $this->stdWrap($icon, $conf['icon.']) : $icon;
         }
         $size = '';
-        if ($conf['size']) {
+        if ($conf['size'] ?? false) {
             $size = isset($conf['size.']) ? $this->stdWrap($theSize, $conf['size.']) : $theSize;
         }
         // Wrapping file label
             $size = isset($conf['size.']) ? $this->stdWrap($theSize, $conf['size.']) : $theSize;
         }
         // Wrapping file label
-        if ($conf['removePrependedNumbers']) {
+        if ($conf['removePrependedNumbers'] ?? false) {
             $theValue = preg_replace('/_[0-9][0-9](\\.[[:alnum:]]*)$/', '\\1', $theValue);
         }
         if (isset($conf['labelStdWrap.'])) {
             $theValue = $this->stdWrap($theValue, $conf['labelStdWrap.']);
         }
         // Wrapping file
             $theValue = preg_replace('/_[0-9][0-9](\\.[[:alnum:]]*)$/', '\\1', $theValue);
         }
         if (isset($conf['labelStdWrap.'])) {
             $theValue = $this->stdWrap($theValue, $conf['labelStdWrap.']);
         }
         // Wrapping file
-        $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
-        if ($conf['combinedLink']) {
+        $wrap = isset($conf['wrap.'])
+            ? $this->stdWrap($conf['wrap'] ?? '', $conf['wrap.'])
+            : ($conf['wrap'] ?? '');
+        if ($conf['combinedLink'] ?? false) {
             $theValue = $icon . $theValue;
             if ($conf['ATagBeforeWrap']) {
                 $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
             $theValue = $icon . $theValue;
             if ($conf['ATagBeforeWrap']) {
                 $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
@@ -4254,7 +3703,7 @@ class ContentObjectRenderer
             // output
             $output = $file . $size;
         } else {
             // output
             $output = $file . $size;
         } else {
-            if ($conf['ATagBeforeWrap']) {
+            if ($conf['ATagBeforeWrap'] ?? false) {
                 $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
             } else {
                 $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
                 $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
             } else {
                 $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
@@ -4314,9 +3763,11 @@ class ContentObjectRenderer
      * @param string $string The string with parts in (where each part is evaluated by ->calc())
      * @return array And array with evaluated values.
      * @see calc(), \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject::makeGifs()
      * @param string $string The string with parts in (where each part is evaluated by ->calc())
      * @return array And array with evaluated values.
      * @see calc(), \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject::makeGifs()
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10. It is solely used in GMENU, which can be handled there directly.
      */
     public function calcIntExplode($delim, $string)
     {
      */
     public function calcIntExplode($delim, $string)
     {
+        trigger_error('calcIntExplode will be removed in TYPO3 v10.', E_USER_DEPRECATED);
         $temp = explode($delim, $string);
         foreach ($temp as $key => $val) {
             $temp[$key] = (int)$this->calc($val);
         $temp = explode($delim, $string);
         foreach ($temp as $key => $val) {
             $temp[$key] = (int)$this->calc($val);
@@ -4344,9 +3795,10 @@ class ContentObjectRenderer
         $valArr = explode($conf['token'], $value);
 
         // return value directly by returnKey. No further processing
         $valArr = explode($conf['token'], $value);
 
         // return value directly by returnKey. No further processing
-        if (!empty($valArr) && (MathUtility::canBeInterpretedAsInteger($conf['returnKey']) || $conf['returnKey.'])) {
+        if (!empty($valArr) && (MathUtility::canBeInterpretedAsInteger($conf['returnKey'] ?? null) || ($conf['returnKey.'] ?? false))
+        ) {
             $key = isset($conf['returnKey.']) ? (int)$this->stdWrap($conf['returnKey'], $conf['returnKey.']) : (int)$conf['returnKey'];
             $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
         }
 
         // return the amount of elements. No further processing
@@ -4371,7 +3823,8 @@ class ContentObjectRenderer
         if ($wrap !== '' || $cObjNumSplitConf !== '') {
             $splitArr['wrap'] = $wrap;
             $splitArr['cObjNum'] = $cObjNumSplitConf;
         if ($wrap !== '' || $cObjNumSplitConf !== '') {
             $splitArr['wrap'] = $wrap;
             $splitArr['cObjNum'] = $cObjNumSplitConf;
-            $splitArr = $GLOBALS['TSFE']->tmpl->splitConfArray($splitArr, $splitCount);
+            $splitArr = GeneralUtility::makeInstance(TypoScriptService::class)
+                ->explodeConfigurationForOptionSplit($splitArr, $splitCount);
         }
         $content = '';
         for ($a = 0; $a < $splitCount; $a++) {
         }
         $content = '';
         for ($a = 0; $a < $splitCount; $a++) {
@@ -4425,9 +3878,13 @@ class ContentObjectRenderer
         if ((isset($configuration['search']) || isset($configuration['search.'])) && (isset($configuration['replace']) || isset($configuration['replace.']))) {
             // Gets the strings
             $search = isset($configuration['search.']) ? $this->stdWrap($configuration['search'], $configuration['search.']) : $configuration['search'];
         if ((isset($configuration['search']) || isset($configuration['search.'])) && (isset($configuration['replace']) || isset($configuration['replace.']))) {
             // Gets the strings
             $search = isset($configuration['search.']) ? $this->stdWrap($configuration['search'], $configuration['search.']) : $configuration['search'];
-            $replace = isset($configuration['replace.']) ? $this->stdWrap($configuration['replace'], $configuration['replace.']) : $configuration['replace'];
+            $replace = isset($configuration['replace.'])
+                ? $this->stdWrap($configuration['replace'] ?? null, $configuration['replace.'])
+                : $configuration['replace'] ?? null;
             // Determines whether regular expression shall be used
             // Determines whether regular expression shall be used
-            if (isset($configuration['useRegExp']) || $configuration['useRegExp.']) {
+            if (isset($configuration['useRegExp'])
+                || (isset($configuration['useRegExp.']) && $configuration['useRegExp.'])
+            ) {
                 $useRegularExpression = isset($configuration['useRegExp.']) ? $this->stdWrap($configuration['useRegExp'], $configuration['useRegExp.']) : $configuration['useRegExp'];
             }
             // Determines whether replace-pattern uses option-split
                 $useRegularExpression = isset($configuration['useRegExp.']) ? $this->stdWrap($configuration['useRegExp'], $configuration['useRegExp.']) : $configuration['useRegExp'];
             }
             // Determines whether replace-pattern uses option-split
@@ -4444,14 +3901,15 @@ 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);
                     $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);
                 } else {
                     // init for replacement
                     $splitCount = preg_match_all($search, $content, $matches);
                 }
                 if (empty($useOptionSplitReplace)) {
                     $content = preg_replace($search, $replace, $content);
                 } else {
                     // init for replacement
                     $splitCount = preg_match_all($search, $content, $matches);
-                    $replaceArray = $this->getTypoScriptFrontendController()->tmpl->splitConfArray([$replace], $splitCount);
+                    $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
+                    $replaceArray = $typoScriptService->explodeConfigurationForOptionSplit([$replace], $splitCount);
                     $replaceCount = 0;
 
                     $replaceCallback = function ($match) use ($replaceArray, $search, &$replaceCount) {
                     $replaceCount = 0;
 
                     $replaceCallback = function ($match) use ($replaceArray, $search, &$replaceCount) {
@@ -4469,7 +3927,8 @@ class ContentObjectRenderer
 
                     // init for replacement
                     $splitCount = preg_match_all($searchPreg, $content, $matches);
 
                     // init for replacement
                     $splitCount = preg_match_all($searchPreg, $content, $matches);
-                    $replaceArray = $this->getTypoScriptFrontendController()->tmpl->splitConfArray([$replace], $splitCount);
+                    $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
+                    $replaceArray = $typoScriptService->explodeConfigurationForOptionSplit([$replace], $splitCount);
                     $replaceCount = 0;
 
                     $replaceCallback = function () use ($replaceArray, $search, &$replaceCount) {
                     $replaceCount = 0;
 
                     $replaceCallback = function () use ($replaceArray, $search, &$replaceCount) {
@@ -4493,8 +3952,12 @@ class ContentObjectRenderer
      */
     protected function round($content, array $conf = [])
     {
      */
     protected function round($content, array $conf = [])
     {
-        $decimals = isset($conf['decimals.']) ? $this->stdWrap($conf['decimals'], $conf['decimals.']) : $conf['decimals'];
-        $type = isset($conf['roundType.']) ? $this->stdWrap($conf['roundType'], $conf['roundType.']) : $conf['roundType'];
+        $decimals = isset($conf['decimals.'])
+            ? $this->stdWrap($conf['decimals'] ?? '', $conf['decimals.'])
+            : ($conf['decimals'] ?? null);
+        $type = isset($conf['roundType.'])
+            ? $this->stdWrap($conf['roundType'] ?? '', $conf['roundType.'])
+            : ($conf['roundType'] ?? null);
         $floatVal = (float)$content;
         switch ($type) {
             case 'ceil':
         $floatVal = (float)$content;
         switch ($type) {
             case 'ceil':
@@ -4521,9 +3984,15 @@ class ContentObjectRenderer
      */
     public function numberFormat($content, $conf)
     {
      */
     public function numberFormat($content, $conf)
     {
-        $decimals = isset($conf['decimals.']) ? (int)$this->stdWrap($conf['decimals'], $conf['decimals.']) : (int)$conf['decimals'];
-        $dec_point = isset($conf['dec_point.']) ? $this->stdWrap($conf['dec_point'], $conf['dec_point.']) : $conf['dec_point'];
-        $thousands_sep = isset($conf['thousands_sep.']) ? $this->stdWrap($conf['thousands_sep'], $conf['thousands_sep.']) : $conf['thousands_sep'];
+        $decimals = isset($conf['decimals.'])
+            ? (int)$this->stdWrap($conf['decimals'] ?? '', $conf['decimals.'])
+            : (int)($conf['decimals'] ?? 0);
+        $dec_point = isset($conf['dec_point.'])
+            ? $this->stdWrap($conf['dec_point'] ?? '', $conf['dec_point.'])
+            : ($conf['dec_point'] ?? null);
+        $thousands_sep = isset($conf['thousands_sep.'])
+            ? $this->stdWrap($conf['thousands_sep'] ?? '', $conf['thousands_sep.'])
+            : ($conf['thousands_sep'] ?? null);
         return number_format((float)$content, $decimals, $dec_point, $thousands_sep);
     }
 
         return number_format((float)$content, $decimals, $dec_point, $thousands_sep);
     }
 
@@ -4558,7 +4027,7 @@ class ContentObjectRenderer
             $conf = $temp_conf['parseFunc.'];
         }
         // Process:
             $conf = $temp_conf['parseFunc.'];
         }
         // Process:
-        if ((string)$conf['externalBlocks'] === '') {
+        if ((string)($conf['externalBlocks'] ?? '') === '') {
             return $this->_parseFunc($theValue, $conf);
         }
         $tags = strtolower(implode(',', GeneralUtility::trimExplode(',', $conf['externalBlocks'])));
             return $this->_parseFunc($theValue, $conf);
         }
         $tags = strtolower(implode(',', GeneralUtility::trimExplode(',', $conf['externalBlocks'])));
@@ -4665,8 +4134,8 @@ class ContentObjectRenderer
         $stripNL = 0;
         $contentAccum = [];
         $contentAccumP = 0;
         $stripNL = 0;
         $contentAccum = [];
         $contentAccumP = 0;
-        $allowTags = strtolower(str_replace(' ', '', $conf['allowTags']));
-        $denyTags = strtolower(str_replace(' ', '', $conf['denyTags']));
+        $allowTags = strtolower(str_replace(' ', '', $conf['allowTags'] ?? ''));
+        $denyTags = strtolower(str_replace(' ', '', $conf['denyTags'] ?? ''));
         $totalLen = strlen($theValue);
         do {
             if (!$inside) {
         $totalLen = strlen($theValue);
         do {
             if (!$inside) {
@@ -4702,7 +4171,7 @@ class ContentObjectRenderer
                     if (!is_array($currentTag)) {
                         // Constants
                         $tsfe = $this->getTypoScriptFrontendController();
                     if (!is_array($currentTag)) {
                         // Constants
                         $tsfe = $this->getTypoScriptFrontendController();
-                        $tmpConstants = $tsfe->tmpl->setup['constants.'];
+                        $tmpConstants = $tsfe->tmpl->setup['constants.'] ?? null;
                         if ($conf['constants'] && is_array($tmpConstants)) {
                             foreach ($tmpConstants as $key => $val) {
                                 if (is_string($val)) {
                         if ($conf['constants'] && is_array($tmpConstants)) {
                             foreach ($tmpConstants as $key => $val) {
                                 if (is_string($val)) {
@@ -4711,7 +4180,7 @@ class ContentObjectRenderer
                             }
                         }
                         // Short
                             }
                         }
                         // Short
-                        if (is_array($conf['short.'])) {
+                        if (isset($conf['short.']) && is_array($conf['short.'])) {
                             $shortWords = $conf['short.'];
                             krsort($shortWords);
                             foreach ($shortWords as $key => $val) {
                             $shortWords = $conf['short.'];
                             krsort($shortWords);
                             foreach ($shortWords as $key => $val) {
@@ -4721,17 +4190,17 @@ class ContentObjectRenderer
                             }
                         }
                         // stdWrap
                             }
                         }
                         // stdWrap
-                        if (is_array($conf['plainTextStdWrap.'])) {
+                        if (isset($conf['plainTextStdWrap.']) && is_array($conf['plainTextStdWrap.'])) {
                             $data = $this->stdWrap($data, $conf['plainTextStdWrap.']);
                         }
                         // userFunc
                             $data = $this->stdWrap($data, $conf['plainTextStdWrap.']);
                         }
                         // userFunc
-                        if ($conf['userFunc']) {
+                        if ($conf['userFunc'] ?? false) {
                             $data = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $data);
                         }
                         // Makelinks: (Before search-words as we need the links to be generated when searchwords go on...!)
                             $data = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $data);
                         }
                         // Makelinks: (Before search-words as we need the links to be generated when searchwords go on...!)
-                        if ($conf['makelinks']) {
+                        if ($conf['makelinks'] ?? false) {
                             $data = $this->http_makelinks($data, $conf['makelinks.']['http.']);
                             $data = $this->http_makelinks($data, $conf['makelinks.']['http.']);
-                            $data = $this->mailto_makelinks($data, $conf['makelinks.']['mailto.']);
+                            $data = $this->mailto_makelinks($data, $conf['makelinks.']['mailto.'] ?? []);
                         }
                         // Search Words:
                         if ($tsfe->no_cache && $conf['sword'] && is_array($tsfe->sWordList) && $tsfe->sWordRegEx) {
                         }
                         // Search Words:
                         if ($tsfe->no_cache && $conf['sword'] && is_array($tsfe->sWordList) && $tsfe->sWordRegEx) {
@@ -4764,7 +4233,9 @@ class ContentObjectRenderer
                             $data = $newstring;
                         }
                     }
                             $data = $newstring;
                         }
                     }
-                    $contentAccum[$contentAccumP] .= $data;
+                    $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
+                        ? $contentAccum[$contentAccumP] . $data
+                        : $data;
                 }
                 $inside = true;
             } else {
                 }
                 $inside = true;
             } else {
@@ -4782,11 +4253,11 @@ class ContentObjectRenderer
                     $tag[0] = substr($tag[0], 1);
                     $tag['out'] = 1;
                 }
                     $tag[0] = substr($tag[0], 1);
                     $tag['out'] = 1;
                 }
-                if ($conf['tags.'][$tag[0]]) {
+                if ($conf['tags.'][$tag[0]] ?? false) {
                     $treated = false;
                     $stripNL = false;
                     // in-tag
                     $treated = false;
                     $stripNL = false;
                     // in-tag
-                    if (!$currentTag && !$tag['out']) {
+                    if (!$currentTag && (!isset($tag['out']) || !$tag['out'])) {
                         // $currentTag (array!) is the tag we are currently processing
                         $currentTag = $tag;
                         $contentAccumP++;
                         // $currentTag (array!) is the tag we are currently processing
                         $currentTag = $tag;
                         $contentAccumP++;
@@ -4797,13 +4268,13 @@ class ContentObjectRenderer
                         }
                     }
                     // out-tag
                         }
                     }
                     // out-tag
-                    if ($currentTag[0] === $tag[0] && $tag['out']) {
+                    if ($currentTag[0] === $tag[0] && isset($tag['out']) && $tag['out']) {
                         $theName = $conf['tags.'][$tag[0]];
                         $theConf = $conf['tags.'][$tag[0] . '.'];
                         // This flag indicates, that NL- (13-10-chars) should be stripped first and last.
                         $theName = $conf['tags.'][$tag[0]];
                         $theConf = $conf['tags.'][$tag[0] . '.'];
                         // This flag indicates, that NL- (13-10-chars) should be stripped first and last.
-                        $stripNL = (bool)$theConf['stripNL'];
+                        $stripNL = (bool)($theConf['stripNL'] ?? false);
                         // This flag indicates, that this TypoTag section should NOT be included in the nonTypoTag content.
                         // This flag indicates, that this TypoTag section should NOT be included in the nonTypoTag content.
-                        $breakOut = (bool)$theConf['breakoutTypoTagContent'];
+                        $breakOut = (bool)($theConf['breakoutTypoTagContent'] ?? false);
                         $this->parameters = [];
                         if ($currentTag[1]) {
                             $params = GeneralUtility::get_tag_attributes($currentTag[1]);
                         $this->parameters = [];
                         if ($currentTag[1]) {
                             $params = GeneralUtility::get_tag_attributes($currentTag[1]);
@@ -4828,7 +4299,10 @@ class ContentObjectRenderer
                         $contentAccumP++;
                         // If the TypoTag section
                         if (!$breakOut) {
                         $contentAccumP++;
                         // If the TypoTag section
                         if (!$breakOut) {
-                            $contentAccum[$contentAccumP - 2] .= $contentAccum[$contentAccumP - 1] . $contentAccum[$contentAccumP];
+                            if (!isset($contentAccum[$contentAccumP - 2])) {
+                                $contentAccum[$contentAccumP - 2] = '';
+                            }
+                            $contentAccum[$contentAccumP - 2] .= ($contentAccum[$contentAccumP - 1] ?? '') . ($contentAccum[$contentAccumP] ?? '');
                             unset($contentAccum[$contentAccumP]);
                             unset($contentAccum[$contentAccumP - 1]);
                             $contentAccumP -= 2;
                             unset($contentAccum[$contentAccumP]);
                             unset($contentAccum[$contentAccumP - 1]);
                             $contentAccumP -= 2;
@@ -4843,10 +4317,14 @@ class ContentObjectRenderer
                 } else {
                     // If a tag was not a typo tag, then it is just added to the content
                     $stripNL = false;
                 } else {
                     // If a tag was not a typo tag, then it is just added to the content
                     $stripNL = false;
-                    if (GeneralUtility::inList($allowTags, $tag[0]) || $denyTags != '*' && !GeneralUtility::inList($denyTags, $tag[0])) {
-                        $contentAccum[$contentAccumP] .= $data;
+                    if (GeneralUtility::inList($allowTags, $tag[0]) || $denyTags !== '*' && !GeneralUtility::inList($denyTags, $tag[0])) {
+                        $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
+                            ? $contentAccum[$contentAccumP] . $data
+                            : $data;
                     } else {
                     } else {
-                        $contentAccum[$contentAccumP] .= htmlspecialchars($data);
+                        $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
+                            ? $contentAccum[$contentAccumP] . htmlspecialchars($data)
+                            : htmlspecialchars($data);
                     }
                 }
                 $inside = false;
                     }
                 }
                 $inside = false;
@@ -4859,11 +4337,11 @@ class ContentObjectRenderer
         for ($a = 0; $a < $contentAccumCount; $a++) {
             if ($a % 2 != 1) {
                 // stdWrap
         for ($a = 0; $a < $contentAccumCount; $a++) {
             if ($a % 2 != 1) {
                 // stdWrap
-                if (is_array($conf['nonTypoTagStdWrap.'])) {
+                if (isset($conf['nonTypoTagStdWrap.']) && is_array($conf['nonTypoTagStdWrap.'])) {
                     $contentAccum[$a] = $this->stdWrap($contentAccum[$a], $conf['nonTypoTagStdWrap.']);
                 }
                 // userFunc
                     $contentAccum[$a] = $this->stdWrap($contentAccum[$a], $conf['nonTypoTagStdWrap.']);
                 }
                 // userFunc
-                if ($conf['nonTypoTagUserFunc']) {
+                if (!empty($conf['nonTypoTagUserFunc'])) {
                     $contentAccum[$a] = $this->callUserFunction($conf['nonTypoTagUserFunc'], $conf['nonTypoTagUserFunc.'], $contentAccum[$a]);
                 }
             }
                     $contentAccum[$a] = $this->callUserFunction($conf['nonTypoTagUserFunc'], $conf['nonTypoTagUserFunc.'], $contentAccum[$a]);
                 }
             }
@@ -4872,7 +4350,7 @@ class ContentObjectRenderer
     }
 
     /**
     }
 
     /**
-     * Lets you split the content by LF and proces each line independently. Used to format content made with the RTE.
+     * Lets you split the content by LF and process each line independently. Used to format content made with the RTE.
      *
      * @param string $theValue The input value
      * @param array $conf TypoScript options
      *
      * @param string $theValue The input value
      * @param array $conf TypoScript options
@@ -4881,15 +4359,23 @@ class ContentObjectRenderer
      */
     public function encaps_lineSplit($theValue, $conf)
     {
      */
     public function encaps_lineSplit($theValue, $conf)
     {
+        if ((string)$theValue === '') {
+            return '';
+        }
         $lParts = explode(LF, $theValue);
         $lParts = explode(LF, $theValue);
+
+        // When the last element is an empty linebreak we need to remove it, otherwise we will have a duplicate empty line.
+        $lastPartIndex = count($lParts) - 1;
+        if ($lParts[$lastPartIndex] === '' && trim($lParts[$lastPartIndex - 1], CR) === '') {
+            array_pop($lParts);
+        }
+
         $encapTags = GeneralUtility::trimExplode(',', strtolower($conf['encapsTagList']), true);
         $nonWrappedTag = $conf['nonWrappedTag'];
         $defaultAlign = isset($conf['defaultAlign.'])
         $encapTags = GeneralUtility::trimExplode(',', strtolower($conf['encapsTagList']), true);
         $nonWrappedTag = $conf['nonWrappedTag'];
         $defaultAlign = isset($conf['defaultAlign.'])
-            ? trim($this->stdWrap($conf['defaultAlign'], $conf['defaultAlign.']))
-            : trim($conf['defaultAlign']);
-        if ((string)$theValue === '') {
-            return '';
-        }
+            ? trim($this->stdWrap($conf['defaultAlign'] ?? '', $conf['defaultAlign.']))
+            : trim($conf['defaultAlign'] ?? '');
+
         $str_content = '';
         foreach ($lParts as $k => $l) {
             $sameBeginEnd = 0;
         $str_content = '';
         foreach ($lParts as $k => $l) {
             $sameBeginEnd = 0;
@@ -4898,7 +4384,7 @@ class ContentObjectRenderer
             $attrib = [];
             $nonWrapped = false;
             $tagName = '';
             $attrib = [];
             $nonWrapped = false;
             $tagName = '';
-            if ($l[0] === '<' && substr($l, -1) === '>') {
+            if (isset($l[0]) && $l[0] === '<' && substr($l, -1) === '>') {
                 $fwParts = explode('>', substr($l, 1), 2);
                 list($tagName) = explode(' ', $fwParts[0], 2);
                 if (!$fwParts[1]) {
                 $fwParts = explode('>', substr($l, 1), 2);
                 list($tagName) = explode(' ', $fwParts[0], 2);
                 if (!$fwParts[1]) {
@@ -4919,7 +4405,7 @@ class ContentObjectRenderer
             }
             if ($sameBeginEnd && in_array(strtolower($tagName), $encapTags)) {
                 $uTagName = strtoupper($tagName);
             }
             if ($sameBeginEnd && in_array(strtolower($tagName), $encapTags)) {
                 $uTagName = strtoupper($tagName);
-                $uTagName = strtoupper($conf['remapTag.'][$uTagName] ? $conf['remapTag.'][$uTagName] : $uTagName);
+                $uTagName = strtoupper($conf['remapTag.'][$uTagName] ?? $uTagName);
             } else {
                 $uTagName = strtoupper($nonWrappedTag);
                 // The line will be wrapped: $uTagName should not be an empty tag
             } else {
                 $uTagName = strtoupper($nonWrappedTag);
                 // The line will be wrapped: $uTagName should not be an empty tag
@@ -4934,11 +4420,11 @@ class ContentObjectRenderer
             }
             if ($uTagName) {
                 // Setting common attributes
             }
             if ($uTagName) {
                 // Setting common attributes
-                if (is_array($conf['addAttributes.'][$uTagName . '.'])) {
+                if (isset($conf['addAttributes.'][$uTagName . '.']) && is_array($conf['addAttributes.'][$uTagName . '.'])) {
                     foreach ($conf['addAttributes.'][$uTagName . '.'] as $kk => $vv) {
                         if (!is_array($vv)) {
                             if ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'blank') {
                     foreach ($conf['addAttributes.'][$uTagName . '.'] as $kk => $vv) {
                         if (!is_array($vv)) {
                             if ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'blank') {
-                                if ((string)$attrib[$kk] === '') {
+                                if ((string)($attrib[$kk] ?? '') === '') {
                                     $attrib[$kk] = $vv;
                                 }
                             } elseif ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'exists') {
                                     $attrib[$kk] = $vv;
                                 }
                             } elseif ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'exists') {
@@ -4952,23 +4438,24 @@ class ContentObjectRenderer
                     }
                 }
                 // Wrapping all inner-content:
                     }
                 }
                 // Wrapping all inner-content:
-                if (is_array($conf['encapsLinesStdWrap.'][$uTagName . '.'])) {
+                if (isset($conf['encapsLinesStdWrap.'][$uTagName . '.']) && is_array($conf['encapsLinesStdWrap.'][$uTagName . '.'])) {
                     $str_content = $this->stdWrap($str_content, $conf['encapsLinesStdWrap.'][$uTagName . '.']);
                 }
                 // Default align
                     $str_content = $this->stdWrap($str_content, $conf['encapsLinesStdWrap.'][$uTagName . '.']);
                 }
                 // Default align
-                if (!$attrib['align'] && $defaultAlign) {
+                if ((!isset($attrib['align']) || !$attrib['align']) && $defaultAlign) {
                     $attrib['align'] = $defaultAlign;
                 }
                     $attrib['align'] = $defaultAlign;
                 }
-                $params = GeneralUtility::implodeAttributes($attrib, 1);
-                if (!($conf['removeWrapping'] && !($emptyTag && $conf['removeWrapping.']['keepSingleTag']))) {
-                    if ($emptyTag) {
+                $params = GeneralUtility::implodeAttributes($attrib, true);
+                if (!isset($conf['removeWrapping']) || !$conf['removeWrapping'] || ($emptyTag && $conf['removeWrapping.']['keepSingleTag'])) {
+                    $selfClosingTagList = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
+                    if ($emptyTag && in_array(strtolower($uTagName), $selfClosingTagList, true)) {
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . ' />';
                     } else {
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . '>' . $str_content . '</' . strtolower($uTagName) . '>';
                     }
                 }
             }
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . ' />';
                     } else {
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . '>' . $str_content . '</' . strtolower($uTagName) . '>';
                     }
                 }
             }
-            if ($nonWrapped && $conf['wrapNonWrappedLines']) {
+            if ($nonWrapped && isset($conf['wrapNonWrappedLines']) && $conf['wrapNonWrappedLines']) {
                 $str_content = $this->wrap($str_content, $conf['wrapNonWrappedLines']);
             }
             $lParts[$k] = $str_content;
                 $str_content = $this->wrap($str_content, $conf['wrapNonWrappedLines']);
             }
             $lParts[$k] = $str_content;
@@ -4990,12 +4477,12 @@ class ContentObjectRenderer
     {
         $aTagParams = $this->getATagParams($conf);
         $textstr = '';
     {
         $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];
             for ($i = 1; $i < $pieces; $i++) {
             $textpieces = explode($scheme, $data);
             $pieces = count($textpieces);
             $textstr = $textpieces[0];
             for ($i = 1; $i < $pieces; $i++) {
-                $len = strcspn($textpieces[$i], chr(32) . TAB . CRLF);
+                $len = strcspn($textpieces[$i], chr(32) . "\t" . CRLF);
                 if (trim(substr($textstr, -1)) === '' && $len) {
                     $lastChar = substr($textpieces[$i], $len - 1, 1);
                     if (!preg_match('/[A-Za-z0-9\\/#_-]/', $lastChar)) {
                 if (trim(substr($textstr, -1)) === '' && $len) {
                     $lastChar = substr($textpieces[$i], $len - 1, 1);
                     if (!preg_match('/[A-Za-z0-9\\/#_-]/', $lastChar)) {
@@ -5035,7 +4522,7 @@ class ContentObjectRenderer
 
                     $res = '<a href="' . htmlspecialchars($linkUrl) . '"'
                         . ($target !== '' ? ' target="' . htmlspecialchars($target) . '"' : '')
 
                     $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'] !== '') {
 
                     $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
                     if ((string)$conf['ATagBeforeWrap'] !== '') {
@@ -5071,7 +4558,7 @@ class ContentObjectRenderer
         $textstr = $textpieces[0];
         $tsfe = $this->getTypoScriptFrontendController();
         for ($i = 1; $i < $pieces; $i++) {
         $textstr = $textpieces[0];
         $tsfe = $this->getTypoScriptFrontendController();
         for ($i = 1; $i < $pieces; $i++) {
-            $len = strcspn($textpieces[$i], chr(32) . TAB . CRLF);
+            $len = strcspn($textpieces[$i], chr(32) . "\t" . CRLF);
             if (trim(substr($textstr, -1)) === '' && $len) {
                 $lastChar = substr($textpieces[$i], $len - 1, 1);
                 if (!preg_match('/[A-Za-z0-9]/', $lastChar)) {
             if (trim(substr($textstr, -1)) === '' && $len) {
                 $lastChar = substr($textpieces[$i], $len - 1, 1);
                 if (!preg_match('/[A-Za-z0-9]/', $lastChar)) {
@@ -5118,7 +4605,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
      *
      * @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)
      * @see IMG_RESOURCE(), cImage(), \TYPO3\CMS\Frontend\Imaging\GifBuilder
      */
     public function getImgResource($file, $fileArray)
@@ -5130,11 +4617,8 @@ class ContentObjectRenderer
             $fileArray = (array)$fileArray;
         }
         $imageResource = null;
             $fileArray = (array)$fileArray;
         }
         $imageResource = null;
-        $tsfe = $this->getTypoScriptFrontendController();
         if ($file === 'GIFBUILDER') {
         if ($file === 'GIFBUILDER') {
-            /** @var GifBuilder $gifCreator */
             $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
             $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
-            $gifCreator->init();
             $theImage = '';
             if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
                 $gifCreator->start($fileArray, $this->data);
             $theImage = '';
             if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
                 $gifCreator->start($fileArray, $this->data);
@@ -5147,12 +4631,9 @@ class ContentObjectRenderer
                 $fileObject = $file;
             } elseif ($file instanceof FileReference) {
                 $fileObject = $file->getOriginalFile();
                 $fileObject = $file;
             } elseif ($file instanceof FileReference) {
                 $fileObject = $file->getOriginalFile();
-                if (!isset($fileArray['crop'])) {
-                    $fileArray['crop'] = $file->getProperty('crop');
-                }
             } else {
                 try {
             } else {
                 try {
-                    if ($fileArray['import.']) {
+                    if (isset($fileArray['import.']) && $fileArray['import.']) {
                         $importedFile = trim($this->stdWrap('', $fileArray['import.']));
                         if (!empty($importedFile)) {
                             $file = $importedFile;
                         $importedFile = trim($this->stdWrap('', $fileArray['import.']));
                         if (!empty($importedFile)) {
                             $file = $importedFile;
@@ -5162,11 +4643,8 @@ class ContentObjectRenderer
                     if (MathUtility::canBeInterpretedAsInteger($file)) {
                         $treatIdAsReference = isset($fileArray['treatIdAsReference.']) ? $this->stdWrap($fileArray['treatIdAsReference'], $fileArray['treatIdAsReference.']) : $fileArray['treatIdAsReference'];
                         if (!empty($treatIdAsReference)) {
                     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'] = $fileReference->getProperty('crop');
-                            }
+                            $file = $this->getResourceFactory()->getFileReferenceObject($file);
+                            $fileObject = $file->getOriginalFile();
                         } else {
                             $fileObject = $this->getResourceFactory()->getFileObject($file);
                         }
                         } else {
                             $fileObject = $this->getResourceFactory()->getFileObject($file);
                         }
@@ -5179,9 +4657,7 @@ class ContentObjectRenderer
                         $fileObject = $this->getResourceFactory()->retrieveFileOrFolderObject($file);
                     }
                 } catch (Exception $exception) {
                         $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;
                 }
             }
                     return null;
                 }
             }
@@ -5197,9 +4673,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['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'])) {
                 // Possibility to cancel/force profile extraction
                 // see $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_stripColorProfileCommand']
                 if (isset($fileArray['stripProfile'])) {
@@ -5222,10 +4701,8 @@ class ContentObjectRenderer
                         $processingConfiguration['maskImages']['maskBottomImageMask'] = $bottomImg_mask['processedFile'];
                     }
                     $processedFileObject = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
                         $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(),
                             0 => $processedFileObject->getProperty('width'),
                             1 => $processedFileObject->getProperty('height'),
                             2 => $processedFileObject->getExtension(),
@@ -5235,27 +4712,24 @@ 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,
                             // 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];
                 }
             }
         }
         // If image was processed by GIFBUILDER:
         // ($imageResource indicates that it was processed the regular way)
         if (!isset($imageResource)) {
                 }
             }
         }
         // If image was processed by GIFBUILDER:
         // ($imageResource indicates that it was processed the regular way)
         if (!isset($imageResource)) {
-            $theImage = $tsfe->tmpl->getFileName($file);
-            if ($theImage) {
-                $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
-                /** @var $gifCreator GifBuilder */
-                $gifCreator->init();
-                $info = $gifCreator->imageMagickConvert($theImage, 'WEB');
+            try {
+                $theImage = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize((string)$file);
+                $info = GeneralUtility::makeInstance(GifBuilder::class)->imageMagickConvert($theImage, 'WEB');
                 $info['origFile'] = $theImage;
                 // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder, ln 100ff in order for the setup-array to create a unique filename hash.
                 $info['origFile_mtime'] = @filemtime($theImage);
                 $imageResource = $info;
                 $info['origFile'] = $theImage;
                 // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder, ln 100ff in order for the setup-array to create a unique filename hash.
                 $info['origFile_mtime'] = @filemtime($theImage);
                 $imageResource = $info;
+            } catch (\TYPO3\CMS\Core\Resource\Exception $e) {
+                // do nothing in case the file path is invalid
             }
         }
         // Hook 'getImgResource': Post-processing of image resources
             }
         }
         // Hook 'getImgResource': Post-processing of image resources
@@ -5268,6 +4742,92 @@ class ContentObjectRenderer
         return $imageResource;
     }
 
         return $imageResource;
     }
 
+    /**
+     * 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 Area
+     */
+    protected function createCropAreaFromJsonString(string $cropSettings, string $cropVariant): Area
+    {
+        return CropVariantCollection::create($cropSettings)->getCropArea($cropVariant);
+    }
+
     /***********************************************
      *
      * Data retrieval etc.
     /***********************************************
      *
      * Data retrieval etc.
@@ -5277,20 +4837,20 @@ class ContentObjectRenderer
      * Returns the value for the field from $this->data. If "//" is found in the $field value that token will split the field values apart and the first field having a non-blank value will be returned.
      *
      * @param string $field The fieldname, eg. "title" or "navtitle // title" (in the latter case the value of $this->data[navtitle] is returned if not blank, otherwise $this->data[title] will be)
      * Returns the value for the field from $this->data. If "//" is found in the $field value that token will split the field values apart and the first field having a non-blank value will be returned.
      *
      * @param string $field The fieldname, eg. "title" or "navtitle // title" (in the latter case the value of $this->data[navtitle] is returned if not blank, otherwise $this->data[title] will be)
-     * @return string
+     * @return string|null
      */
     public function getFieldVal($field)
     {
         if (!strstr($field, '//')) {
      */
     public function getFieldVal($field)
     {
         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];
-                }
+            return $this->data[trim($field)] ?? null;
+        }
+        $sections = GeneralUtility::trimExplode('//', $field, true);
+        foreach ($sections as $k) {
+            if ((string)$this->data[$k] !== '') {
+                return $this->data[$k];
             }
         }
             }
         }
+
         return '';
     }
 
         return '';
     }
 
@@ -5298,7 +4858,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)
      * 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()
      */
      * @return string The value fetched
      * @see getFieldVal()
      */
@@ -5317,7 +4877,7 @@ class ContentObjectRenderer
             $parts = explode(':', $secVal, 2);
             $type = strtolower(trim($parts[0]));
             $typesWithOutParameters = ['level', 'date', 'current', 'pagelayout'];
             $parts = explode(':', $secVal, 2);
             $type = strtolower(trim($parts[0]));
             $typesWithOutParameters = ['level', 'date', 'current', 'pagelayout'];
-            $key = trim($parts[1]);
+            $key = trim($parts[1] ?? '');
             if (($key != '') || in_array($type, $typesWithOutParameters)) {
                 switch ($type) {
                     case 'gp':
             if (($key != '') || in_array($type, $typesWithOutParameters)) {
                 switch ($type) {
                     case 'gp':
@@ -5345,7 +4905,7 @@ class ContentObjectRenderer
                         $retVal = $this->parameters[$key];
                         break;
                     case 'register':
                         $retVal = $this->parameters[$key];
                         break;
                     case 'register':
-                        $retVal = $tsfe->register[$key];
+                        $retVal = $tsfe->register[$key] ?? null;
                         break;
                     case 'global':
                         $retVal = $this->getGlobal($key);
                         break;
                     case 'global':
                         $retVal = $this->getGlobal($key);
@@ -5356,12 +4916,12 @@ class ContentObjectRenderer
                     case 'leveltitle':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
                     case 'leveltitle':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
-                        $retVal = $this->rootLineValue($numericKey, 'title', strtolower($keyParts[1]) === 'slide');
+                        $retVal = $this->rootLineValue($numericKey, 'title', strtolower($keyParts[1] ?? '') === 'slide');
                         break;
                     case 'levelmedia':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
                         break;
                     case 'levelmedia':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
-                        $retVal = $this->rootLineValue($numericKey, 'media', strtolower($keyParts[1]) === 'slide');
+                        $retVal = $this->rootLineValue($numericKey, 'media', strtolower($keyParts[1] ?? '') === 'slide');
                         break;
                     case 'leveluid':
                         $numericKey = $this->getKey($key, $tsfe->tmpl->rootLine);
                         break;
                     case 'leveluid':
                         $numericKey = $this->getKey($key, $tsfe->tmpl->rootLine);
@@ -5370,13 +4930,13 @@ class ContentObjectRenderer
                     case 'levelfield':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
                     case 'levelfield':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
-                        $retVal = $this->rootLineValue($numericKey, $keyParts[1], strtolower($keyParts[2]) === 'slide');
+                        $retVal = $this->rootLineValue($numericKey, $keyParts[1], strtolower($keyParts[2] ?? '') === 'slide');
                         break;
                     case 'fullrootline':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $fullKey = (int)$keyParts[0] - count($tsfe->tmpl->rootLine) + count($tsfe->rootLine);
                         if ($fullKey >= 0) {
                         break;
                     case 'fullrootline':
                         $keyParts = GeneralUtility::trimExplode(',', $key);
                         $fullKey = (int)$keyParts[0] - count($tsfe->tmpl->rootLine) + count($tsfe->rootLine);
                         if ($fullKey >= 0) {
-                            $retVal = $this->rootLineValue($fullKey, $keyParts[1], stristr($keyParts[2], 'slide'), $tsfe->rootLine);
+                            $retVal = $this->rootLineValue($fullKey, $keyParts[1], stristr($keyParts[2] ?? '', 'slide'), $tsfe->rootLine);
                         }
                         break;
                     case 'date':
                         }
                         break;
                     case 'date':
@@ -5407,12 +4967,13 @@ class ContentObjectRenderer
                             $rootLine = $tsfe->rootLine;
                             array_shift($rootLine);
                             foreach ($rootLine as $rootLinePage) {
                             $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;
                                 // 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;
                                 }
                                     // Stop searching if a layout for "next level" is set
                                     break;
                                 }
@@ -5423,7 +4984,7 @@ class ContentObjectRenderer
                         }
                         break;
                     case 'current':
                         }
                         break;
                     case 'current':
-                        $retVal = $this->data[$this->currentValKey];
+                        $retVal = $this->data[$this->currentValKey] ?? null;
                         break;
                     case 'db':
                         $selectParts = GeneralUtility::trimExplode(':', $key);
                         break;
                     case 'db':
                         $selectParts = GeneralUtility::trimExplode(':', $key);
@@ -5436,7 +4997,12 @@ class ContentObjectRenderer
                         $retVal = $tsfe->sL('LLL:' . $key);
                         break;
                     case 'path':
                         $retVal = $tsfe->sL('LLL:' . $key);
                         break;
                     case 'path':
-                        $retVal = $tsfe->tmpl->getFileName($key);
+                        try {
+                            $retVal = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($key);
+                        } catch (\TYPO3\CMS\Core\Resource\Exception $e) {
+                            // do nothing in case the file path is invalid
+                            $retVal = null;
+                        }
                         break;
                     case 'cobj':
                         switch ($key) {
                         break;
                     case 'cobj':
                         switch ($key) {
@@ -5476,16 +5042,43 @@ class ContentObjectRenderer
                             }
                         }
                         break;
                             }
                         }
                         break;
+                    case 'session':
+                        $keyParts = GeneralUtility::trimExplode('|', $key, true);
+                        $sessionKey = array_shift($keyParts);
+                        $retVal = $this->getTypoScriptFrontendController()->fe_user->getSessionData($sessionKey);
+                        foreach ($keyParts as $keyPart) {
+                            if (is_object($retVal)) {
+                                $retVal = $retVal->{$keyPart};
+                            } elseif (is_array($retVal)) {
+                                $retVal = $retVal[$keyPart];
+                            } else {
+                                $retVal = '';
+                                break;
+                            }
+                        }
+                        if (!is_scalar($retVal)) {
+                            $retVal = '';
+                        }
+                        break;
+                    case 'context':
+                        $context = GeneralUtility::makeInstance(Context::class);
+                        list($aspectName, $propertyName) = GeneralUtility::trimExplode(':', $key, true, 2);
+                        $retVal = $context->getPropertyFromAspect($aspectName, $propertyName, '');
+                        if (is_array($retVal)) {
+                            $retVal = implode(',', $retVal);
+                        }
+                        if (!is_scalar($retVal)) {
+                            $retVal = '';
+                        }
                 }
             }
                 }
             }
-            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;
             }
         }
         return $retVal;
@@ -5514,9 +5107,7 @@ class ContentObjectRenderer
                 $fileObject = null;
             }
         } catch (Exception $exception) {
                 $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;
         }
 
             $fileObject = null;
         }
 
@@ -5573,14 +5164,14 @@ class ContentObjectRenderer
         $rootLine = is_array($altRootLine) ? $altRootLine : $this->getTypoScriptFrontendController()->tmpl->rootLine;
         if (!$slideBack) {
             return $rootLine[$key][$field];
         $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 '';
     }
 
         return '';
     }
 
@@ -5640,40 +5231,6 @@ class ContentObjectRenderer
         return $key;
     }
 
         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)
     /***********************************************
      *
      * Link functions (typolink)
@@ -5689,8 +5246,10 @@ class ContentObjectRenderer
      * @param string $linkText The string (text) to link
      * @param string $mixedLinkParameter destination data like "15,13 _blank myclass &more=1" used to create the link
      * @param array $configuration TypoScript configuration
      * @param string $linkText The string (text) to link
      * @param string $mixedLinkParameter destination data like "15,13 _blank myclass &more=1" used to create the link
      * @param array $configuration TypoScript configuration
-     * @return array | string
+     * @return array|string
      * @see typoLink()
      * @see typoLink()
+     *
+     * @todo the functionality of the "file:" syntax + the hook should be marked as deprecated, an upgrade wizard should handle existing links
      */
     protected function resolveMixedLinkParameter($linkText, $mixedLinkParameter, &$configuration = [])
     {
      */
     protected function resolveMixedLinkParameter($linkText, $mixedLinkParameter, &$configuration = [])
     {
@@ -5699,10 +5258,14 @@ class ContentObjectRenderer
         // Link parameter value = first part
         $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($mixedLinkParameter);
 
         // Link parameter value = first part
         $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($mixedLinkParameter);
 
-        // Check for link-handler keyword:
-        list($linkHandlerKeyword, $linkHandlerValue) = explode(':', $linkParameterParts['url'], 2);
-        if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword] && (string)$linkHandlerValue !== '') {
-            $linkHandlerObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]);
+        // Check for link-handler keyword
+        $linkHandlerExploded = explode(':', $linkParameterParts['url'], 2);
+        $linkHandlerKeyword = $linkHandlerExploded[0] ?? null;
+        $linkHandlerValue = $linkHandlerExploded[1] ?? null;
+        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword])
+            && (string)$linkHandlerValue !== ''
+        ) {
+            $linkHandlerObj = GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]);
             if (method_exists($linkHandlerObj, 'main')) {
                 return $linkHandlerObj->main($linkText, $configuration, $linkHandlerKeyword, $linkHandlerValue, $mixedLinkParameter, $this);
             }
             if (method_exists($linkHandlerObj, 'main')) {
                 return $linkHandlerObj->main($linkText, $configuration, $linkHandlerKeyword, $linkHandlerValue, $mixedLinkParameter, $this);
             }
@@ -5725,8 +5288,8 @@ class ContentObjectRenderer
                 // Resource was not found
                 return $linkText;
             }
                 // Resource was not found
                 return $linkText;
             }
-        // Disallow direct javascript: or data: links
         } elseif (in_array(strtolower(trim($linkHandlerKeyword)), ['javascript', 'data'], true)) {
         } elseif (in_array(strtolower(trim($linkHandlerKeyword)), ['javascript', 'data'], true)) {
+            // Disallow direct javascript: or data: links
             return $linkText;
         } else {
             $linkParameter = $linkParameterParts['url'];
             return $linkText;
         } else {
             $linkParameter = $linkParameterParts['url'];
@@ -5766,10 +5329,11 @@ class ContentObjectRenderer
         $linkText = (string)$linkText;
         $tsfe = $this->getTypoScriptFrontendController();
 
         $linkText = (string)$linkText;
         $tsfe = $this->getTypoScriptFrontendController();
 
-        $LD = [];
-        $finalTagParts = [];
-        $finalTagParts['aTagParams'] = $this->getATagParams($conf);
-        $linkParameter = trim(isset($conf['parameter.']) ? $this->stdWrap($conf['parameter'], $conf['parameter.']) : $conf['parameter']);
+        $linkParameter = trim(
+            (isset($conf['parameter.']) ?? '')
+            ? $this->stdWrap($conf['parameter'] ?? '', $conf['parameter.'])
+            : ($conf['parameter'] ?? '')
+        );
         $this->lastTypoLinkUrl = '';
         $this->lastTypoLinkTarget = '';
 
         $this->lastTypoLinkUrl = '';
         $this->lastTypoLinkTarget = '';
 
@@ -5778,16 +5342,70 @@ class ContentObjectRenderer
         if (!is_array($resolvedLinkParameters)) {
             return $resolvedLinkParameters;
         }
         if (!is_array($resolvedLinkParameters)) {
             return $resolvedLinkParameters;
         }
-
         $linkParameter = $resolvedLinkParameters['href'];
         $target = $resolvedLinkParameters['target'];
         $linkParameter = $resolvedLinkParameters['href'];
         $target = $resolvedLinkParameters['target'];
-        $linkClass = $resolvedLinkParameters['class'];
         $title = $resolvedLinkParameters['title'];
 
         if (!$linkParameter) {
             return $linkText;
         }
 
         $title = $resolvedLinkParameters['title'];
 
         if (!$linkParameter) {
             return $linkText;
         }
 
+        // Detecting kind of link and resolve all necessary parameters
+        $linkService = GeneralUtility::makeInstance(LinkService::class);
+        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($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']],
+                $this
+            );
+            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;
+        }
+
+        $finalTagParts = [
+            'aTagParams' => $this->getATagParams($conf) . $this->extLinkATagParams($this->lastTypoLinkUrl, $linkDetails['type']),
+            'url'        => $this->lastTypoLinkUrl,
+            'TYPE'       => $linkDetails['type']
+        ];
+
+        // Ensure "href" is not in the list of aTagParams to avoid double tags, usually happens within buggy parseFunc settings
+        if (!empty($finalTagParts['aTagParams'])) {
+            $aTagParams = GeneralUtility::get_tag_attributes($finalTagParts['aTagParams']);
+            if (isset($aTagParams['href'])) {
+                unset($aTagParams['href']);
+                $finalTagParts['aTagParams'] = GeneralUtility::implodeAttributes($aTagParams);
+            }
+        }
+
+        // Building the final <a href=".."> tag
+        $tagAttributes = [];
+
+        // Title attribute
+        if (empty($title)) {
+            $title = $conf['title'] ?? '';
+            if (isset($conf['title.']) && is_array($conf['title.'])) {
+                $title = $this->stdWrap($title, $conf['title.']);
+            }
+        }
+
         // Check, if the target is coded as a JS open window link:
         $JSwindowParts = [];
         $JSwindowParams = '';
         // Check, if the target is coded as a JS open window link:
         $JSwindowParts = [];
         $JSwindowParams = '';
@@ -5804,410 +5422,65 @@ class ContentObjectRenderer
             $JSwindow_paramsArr['height'] = 'height=' . $JSwindowParts[2];
             // Imploding into string:
             $JSwindowParams = implode(',', $JSwindow_paramsArr);
             $JSwindow_paramsArr['height'] = 'height=' . $JSwindowParts[2];
             // Imploding into string:
             $JSwindowParams = implode(',', $JSwindow_paramsArr);
-            // Resetting the target since we will use onClick.
-            $target = '';
         }
         }
-
-        // Detecting kind of link and resolve all necessary parameters
-        /** @var LinkService $linkService */
-        $linkService = GeneralUtility::makeInstance(LinkService::class);
-        $linkDetails = $linkService->resolve($linkParameter);
-        switch ($linkDetails['type']) {
-            // If it's a mail address
-            case LinkService::TYPE_EMAIL:
-                list($this->lastTypoLinkUrl, $linkText) = $this->getMailTo($linkDetails['email'], $linkText);
-                $finalTagParts['url'] = $this->lastTypoLinkUrl;
-            break;
-
-            // URL (external)
-            case LinkService::TYPE_URL:
-                if (empty($target)) {
-                    if (isset($conf['extTarget'])) {
-                        $target = $conf['extTarget'];
-                    } elseif ($tsfe->dtdAllowsFrames) {
-                        $target = $tsfe->extTarget;
-                    }
-                    if ($conf['extTarget.']) {
-                        $target = $this->stdWrap($target, $conf['extTarget.']);
-                    }
-                }
-                $linkText = $this->parseFallbackLinkTextIfLinkTextIsEmpty($linkText, $linkDetails['url']);
-
-                $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_EXTERNAL, $linkDetails['url'], $conf);
-                $this->lastTypoLinkTarget = $target;
-                $finalTagParts['url'] = $this->lastTypoLinkUrl;
-                $finalTagParts['targetParams'] = $target ? ' target="' . htmlspecialchars($target) . '"' : '';
-                $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], LinkService::TYPE_URL);
-            break;
-
-            // File (internal)
-            case LinkService::TYPE_FILE:
-            case LinkService::TYPE_FOLDER:
-                $fileOrFolderObject = $linkDetails['file'] ? $linkDetails['file'] : $linkDetails['folder'];
-                // check if the file exists or if a / is contained (same check as in detectLinkType)
-                if ($fileOrFolderObject instanceof FileInterface || $fileOrFolderObject instanceof Folder) {
-                    $linkLocation = $fileOrFolderObject->getPublicUrl();
-                    // Setting title if blank value to link
-                    $linkText = $this->parseFallbackLinkTextIfLinkTextIsEmpty($linkText, rawurldecode($linkLocation));
-                    $linkLocation = (strpos($linkLocation, '/') !== 0 ? $tsfe->absRefPrefix : '') . $linkLocation;
-                    $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_FILE, $linkLocation, $conf);
-                    $this->lastTypoLinkUrl = $this->forceAbsoluteUrl($this->lastTypoLinkUrl, $conf);
-
-                    if (empty($target)) {
-                        $target = isset($conf['fileTarget']) ? $conf['fileTarget'] : $tsfe->fileTarget;
-                        if ($conf['fileTarget.']) {
-                            $target = $this->stdWrap($target, $conf['fileTarget.']);
-                        }
-                    }
-                    $this->lastTypoLinkTarget = $target;
-                    $finalTagParts['url'] = $this->lastTypoLinkUrl;
-                    $finalTagParts['targetParams'] = $target ? ' target="' . htmlspecialchars($target) . '"' : '';
-                    $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], LinkService::TYPE_FILE);
-                } else {
-                    $this->getTimeTracker()->setTSlogMessage('typolink(): File "' . $linkParameter . '" did not exist, so "' . $linkText . '" was not linked.', 1);
-                    return $linkText;
-                }
-            break;
-
-            // Link to a page
-            case LinkService::TYPE_PAGE:
-                $enableLinksAcrossDomains = $tsfe->config['config']['typolinkEnableLinksAcrossDomains'];
-                if ($conf['no_cache.']) {
-                    $conf['no_cache'] = $this->stdWrap($conf['no_cache'], $conf['no_cache.']);
-                }
-                // Checking if the id-parameter is an alias.
-                if (!empty($linkDetails['pagealias'])) {
-                    $linkDetails['pageuid'] = $tsfe->sys_page->getPageIdFromAlias($linkDetails['pagealias']);
-                } elseif (empty($linkDetails['pageuid']) || $linkDetails['pageuid'] === 'current') {
-                    // If no id or alias is given
-                    $linkDetails['pageuid'] = $tsfe->id;
-                }
-                $sectionMark = trim(isset($conf['section.']) ? $this->stdWrap($conf['section'], $conf['section.']) : $conf['section']);
-                if ($sectionMark === '' && isset($linkDetails['fragment'])) {
-                    $sectionMark = $linkDetails['fragment'];
-                }
-                if ($sectionMark !== '') {
-                    $sectionMark = '#' . (MathUtility::canBeInterpretedAsInteger($sectionMark) ? 'c' : '') . $sectionMark;
-                }
-                // Overruling 'type'
-                $pageType = $linkDetails['pagetype'] ?? 0;
-
-                if (isset($linkDetails['parameters'])) {
-                    $conf['additionalParams'] .= '&' . ltrim($linkDetails['parameters'], '&');
-                }
-
-                // Link to page even if access is missing?
-                if (isset($conf['linkAccessRestrictedPages'])) {
-                    $disableGroupAccessCheck = (bool)$conf['linkAccessRestrictedPages'];
-                } else {
-                    $disableGroupAccessCheck = (bool)$tsfe->config['config']['typolinkLinkAccessRestrictedPages'];
-                }
-                // Looking up the page record to verify its existence:
-                $page = $tsfe->sys_page->getPage($linkDetails['pageuid'], $disableGroupAccessCheck);
-                if (!empty($page)) {
-                    // MointPoints, look for closest MPvar:
-                    $MPvarAcc = [];
-                    if (!$tsfe->config['config']['MP_disableTypolinkClosestMPvalue']) {
-                        $temp_MP = $this->getClosestMPvalueForPage($page['uid'], true);
-                        if ($temp_MP) {
-                            $MPvarAcc['closest'] = $temp_MP;
-                        }
-                    }
-                    // Look for overlay Mount Point:
-                    $mount_info = $tsfe->sys_page->getMountPointInfo($page['uid'], $page);
-                    if (is_array($mount_info) && $mount_info['overlay']) {
-                        $page = $tsfe->sys_page->getPage($mount_info['mount_pid'], $disableGroupAccessCheck);
-                        if (empty($page)) {
-                            $this->getTimeTracker()->setTSlogMessage('typolink(): Mount point "' . $mount_info['mount_pid'] . '" was not available, so "' . $linkText . '" was not linked.', 1);
-                            return $linkText;
-                        }
-                        $MPvarAcc['re-map'] = $mount_info['MPvar'];
-                    }
-                    // Setting title if blank value to link
-                    $linkText = $this->parseFallbackLinkTextIfLinkTextIsEmpty($linkText, $page['title']);
-                    // Query Params:
-                    $addQueryParams = $conf['addQueryString'] ? $this->getQueryArguments($conf['addQueryString.']) : '';
-                    $addQueryParams .= isset($conf['additionalParams.']) ? trim($this->stdWrap($conf['additionalParams'], $conf['additionalParams.'])) : trim($conf['additionalParams']);
-                    if ($addQueryParams === '&' || $addQueryParams[0] !== '&') {
-                        $addQueryParams = '';
-                    }
-                    $targetDomain = '';
-                    $currentDomain = (string)$this->getEnvironmentVariable('HTTP_HOST');
-                    // Mount pages are always local and never link to another domain
-                    if (!empty($MPvarAcc)) {
-                        // Add "&MP" var:
-                        $addQueryParams .= '&MP=' . rawurlencode(implode(',', $MPvarAcc));
-                    } elseif (strpos($addQueryParams, '&MP=') === false && $tsfe->config['config']['typolinkCheckRootline']) {
-                        // We do not come here if additionalParams had '&MP='. This happens when typoLink is called from
-                        // menu. Mount points always work in the content of the current domain and we must not change
-                        // domain if MP variables exist.
-                        // If we link across domains and page is free type shortcut, we must resolve the shortcut first!
-                        // If we do not do it, TYPO3 will fail to (1) link proper page in RealURL/CoolURI because
-                        // they return relative links and (2) show proper page if no RealURL/CoolURI exists when link is clicked
-                        if ($enableLinksAcrossDomains
-                            && (int)$page['doktype'] === PageRepository::DOKTYPE_SHORTCUT
-                            && (int)$page['shortcut_mode'] === PageRepository::SHORTCUT_MODE_NONE
-                        ) {
-                            // Save in case of broken destination or endless loop
-                            $page2 = $page;
-                            // Same as in RealURL, seems enough
-                            $maxLoopCount = 20;
-                            while ($maxLoopCount
-                                && is_array($page)
-                                && (int)$page['doktype'] === PageRepository::DOKTYPE_SHORTCUT
-                                && (int)$page['shortcut_mode'] === PageRepository::SHORTCUT_MODE_NONE
-                            ) {
-                                $page = $tsfe->sys_page->getPage($page['shortcut'], $disableGroupAccessCheck);
-                                $maxLoopCount--;
-                            }
-                            if (empty($page) || $maxLoopCount === 0) {
-                                // We revert if shortcut is broken or maximum number of loops is exceeded (indicates endless loop)
-                                $page = $page2;
-                            }
-                        }
-
-                        $targetDomain = $tsfe->getDomainNameForPid($page['uid']);
-                        // Do not prepend the domain if it is the current hostname
-                        if (!$targetDomain || $tsfe->domainNameMatchesCurrentRequest($targetDomain)) {
-                            $targetDomain = '';
-                        }
-                    }
-                    if ($conf['useCacheHash']) {
-                        $params = $tsfe->linkVars . $addQueryParams . '&id=' . $page['uid'];
-                        if (trim($params, '& ') != '') {
-                            /** @var $cacheHash CacheHashCalculator */
-                            $cacheHash = GeneralUtility::makeInstance(CacheHashCalculator::class);
-                            $cHash = $cacheHash->generateForParameters($params);
-                            $addQueryParams .= $cHash ? '&cHash=' . $cHash : '';
-                        }
-                        unset($params);
-                    }
-                    $absoluteUrlScheme = 'http';
-                    // URL shall be absolute:
-                    if (isset($conf['forceAbsoluteUrl']) && $conf['forceAbsoluteUrl'] || $page['url_scheme'] > 0) {
-                        // Override scheme:
-                        if (isset($conf['forceAbsoluteUrl.']['scheme']) && $conf['forceAbsoluteUrl.']['scheme']) {
-                            $absoluteUrlScheme = $conf['forceAbsoluteUrl.']['scheme'];
-                        } elseif ($page['url_scheme'] > 0) {
-                            $absoluteUrlScheme = (int)$page['url_scheme'] === HttpUtility::SCHEME_HTTP ? 'http' : 'https';
-                        } elseif ($this->getEnvironmentVariable('TYPO3_SSL')) {
-                            $absoluteUrlScheme = 'https';
-                        }
-                        // If no domain records are defined, use current domain:
-                        $currentUrlScheme = parse_url($this->getEnvironmentVariable('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
-                        if ($targetDomain === '' && ($conf['forceAbsoluteUrl'] || $absoluteUrlScheme !== $currentUrlScheme)) {
-                            $targetDomain = $currentDomain;
-                        }
-                        // If go for an absolute link, add site path if it's not taken care about by absRefPrefix
-                        if (!$tsfe->config['config']['absRefPrefix'] && $targetDomain === $currentDomain) {
-                            $targetDomain = $currentDomain . rtrim($this->getEnvironmentVariable('TYPO3_SITE_PATH'), '/');
-                        }
-                    }
-                    // If target page has a different domain and the current domain's linking scheme (e.g. RealURL/...) should not be used
-                    if ($targetDomain !== '' && $targetDomain !== $currentDomain && !$enableLinksAcrossDomains) {
-                        if (empty($target)) {
-                            $target = isset($conf['extTarget']) ? $conf['extTarget'] : $tsfe->extTarget;
-                            if ($conf['extTarget.']) {
-                                $target = $this->stdWrap($target, $conf['extTarget.']);
-                            }
-                        }
-                        $LD['target'] = $target;
-                        // Convert IDNA-like domain (if any)
-                        if (!preg_match('/^[a-z0-9.\\-]*$/i', $targetDomain)) {
-                            $targetDomain =  GeneralUtility::idnaEncode($targetDomain);
-                        }
-                        $this->lastTypoLinkUrl = $this->URLqMark($absoluteUrlScheme . '://' . $targetDomain . '/index.php?id=' . $page['uid'], $addQueryParams) . $sectionMark;
-                    } else {
-                        // Internal link or current domain's linking scheme should be used
-                        // Internal target:
-                        if (empty($target)) {
-                            if (isset($conf['target'])) {
-                                $target = $conf['target'];
-                            } elseif ($tsfe->dtdAllowsFrames) {
-                                $target = $tsfe->intTarget;
-                            }
-                            if ($conf['target.']) {
-                                $target = $this->stdWrap($target, $conf['target.']);
-                            }
-                        }
-                        $LD = $tsfe->tmpl->linkData($page, $target, $conf['no_cache'], '', '', $addQueryParams, $pageType, $targetDomain);
-                        if ($targetDomain !== '') {
-                            // We will add domain only if URL does not have it already.
-                            if ($enableLinksAcrossDomains && $targetDomain !== $currentDomain) {
-                                // Get rid of the absRefPrefix if necessary. absRefPrefix is applicable only
-                                // to the current web site. If we have domain here it means we link across
-                                // domains. absRefPrefix can contain domain name, which will screw up
-                                // the link to the external domain.
-                                $prefixLength = strlen($tsfe->config['config']['absRefPrefix']);
-                                if (substr($LD['totalURL'], 0, $prefixLength) === $tsfe->config['config']['absRefPrefix']) {
-                                    $LD['totalURL'] = substr($LD['totalURL'], $prefixLength);
-                                }
-                            }
-                            $urlParts = parse_url($LD['totalURL']);
-                            if (empty($urlParts['host'])) {
-                                $LD['totalURL'] = $absoluteUrlScheme . '://' . $targetDomain . ($LD['totalURL'][0] === '/' ? '' : '/') . $LD['totalURL'];
-                            }
-                        }
-                        $this->lastTypoLinkUrl = $LD['totalURL'] . $sectionMark;
-                    }
-                    $this->lastTypoLinkTarget = $LD['target'];
-                    // If sectionMark is set, there is no baseURL AND the current page is the page the link is to, check if there are any additional parameters or addQueryString parameters and if not, drop the url.
-                    if ($sectionMark
-                        && !$tsfe->config['config']['baseURL']
-                        && (int)$page['uid'] === (int)$tsfe->id
-                        && !trim($addQueryParams)
-                        && (empty($conf['addQueryString']) || !isset($conf['addQueryString.']))
-                    ) {
-                        $currentQueryParams = $this->getQueryArguments([]);
-                        if (!trim($currentQueryParams)) {
-                            list(, $URLparams) = explode('?', $this->lastTypoLinkUrl);
-                            list($URLparams) = explode('#', $URLparams);
-                            parse_str($URLparams . $LD['orig_type'], $URLparamsArray);
-                            // Type nums must match as well as page ids
-                            if ((int)$URLparamsArray['type'] === (int)$tsfe->type) {
-                                unset($URLparamsArray['id']);
-                                unset($URLparamsArray['type']);
-                                // If there are no parameters left.... set the new url.
-                                if (empty($URLparamsArray)) {
-                                    $this->lastTypoLinkUrl = $sectionMark;
-                                }
-                            }
-                        }
-                    }
-                    // If link is to an access restricted page which should be redirected, then find new URL:
-                    if (empty($conf['linkAccessRestrictedPages'])
-                        && $tsfe->config['config']['typolinkLinkAccessRestrictedPages']
-                        && $tsfe->config['config']['typolinkLinkAccessRestrictedPages'] !== 'NONE'
-                        && !$tsfe->checkPageGroupAccess($page)
-                    ) {
-                        $thePage = $tsfe->sys_page->getPage($tsfe->config['config']['typolinkLinkAccessRestrictedPages']);
-                        $addParams = str_replace(
-                            [
-                                '###RETURN_URL###',
-                                '###PAGE_ID###'
-                            ],
-                            [
-                                rawurlencode($this->lastTypoLinkUrl),
-                                $page['uid']
-                            ],
-                            $tsfe->config['config']['typolinkLinkAccessRestrictedPages_addParams']
-                        );
-                        $this->lastTypoLinkUrl = $this->getTypoLink_URL($thePage['uid'] . ($pageType ? ',' . $pageType : ''), $addParams, $target);
-                        $this->lastTypoLinkUrl = $this->forceAbsoluteUrl($this->lastTypoLinkUrl, $conf);
-                        $this->lastTypoLinkLD['totalUrl'] = $this->lastTypoLinkUrl;
-                        $LD = $this->lastTypoLinkLD;
-                    }
-                    // Rendering the tag.
-                    $finalTagParts['url'] = $this->lastTypoLinkUrl;
-                    $finalTagParts['targetParams'] = (string)$LD['target'] !== '' ? ' target="' . htmlspecialchars($LD['target']) . '"' : '';
-                } else {
-                    $this->getTimeTracker()->setTSlogMessage('typolink(): Page id "' . $linkParameter . '" was not found, so "' . $linkText . '" was not linked.', 1);
-                    return $linkText;
-                }
-            break;
-
-            // Legacy files or something else
-            case LinkService::TYPE_UNKNOWN:
-                if ($linkDetails['file']) {
-                    $linkLocation = $linkDetails['file'];
-                    // Setting title if blank value to link
-                    $linkText = $this->parseFallbackLinkTextIfLinkTextIsEmpty($linkText, rawurldecode($linkLocation));
-                    $linkLocation = (strpos($linkLocation, '/') !== 0 ? $tsfe->absRefPrefix : '') . $linkLocation;
-                    $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_FILE, $linkLocation, $conf);
-                    $this->lastTypoLinkUrl = $this->forceAbsoluteUrl($this->lastTypoLinkUrl, $conf);
-                    if (empty($target)) {
-                        $target = isset($conf['fileTarget']) ? $conf['fileTarget'] : $tsfe->fileTarget;
-                        if ($conf['fileTarget.']) {
-                            $target = $this->stdWrap($target, $conf['fileTarget.']);
-                        }
-                    }
-                    $this->lastTypoLinkTarget = $target;
-                    $finalTagParts['url'] = $this->lastTypoLinkUrl;
-                    $finalTagParts['targetParams'] = $target ? ' target="' . $target . '"' : '';
-                    $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], LinkService::TYPE_FILE);
-                } elseif ($linkDetails['url']) {
-                    if (empty($target)) {
-                        if (isset($conf['extTarget'])) {
-                            $target = $conf['extTarget'];
-                        } elseif ($tsfe->dtdAllowsFrames) {
-                            $target = $tsfe->extTarget;
-                        }
-                        if ($conf['extTarget.']) {
-                            $target = $this->stdWrap($target, $conf['extTarget.']);
-                        }
-                    }
-                    $linkText = $this->parseFallbackLinkTextIfLinkTextIsEmpty($linkText, $linkDetails['url']);
-
-                    $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_EXTERNAL, $linkDetails['url'], $conf);
-                    $this->lastTypoLinkTarget = $target;
-                    $finalTagParts['url'] = $this->lastTypoLinkUrl;
-                    $finalTagParts['targetParams'] = $target ? ' target="' . $target . '"' : '';
-                    $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], LinkService::TYPE_URL);
-                }
-            break;
+        if (!$JSwindowParams && $linkDetails['type'] === LinkService::TYPE_EMAIL && $tsfe->spamProtectEmailAddresses === 'ascii') {
+            $tagAttributes['href'] = $finalTagParts['url'];
+        } else {
+            $tagAttributes['href'] = htmlspecialchars($finalTagParts['url']);
+        }
+        if (!empty($title)) {
+            $tagAttributes['title'] = htmlspecialchars($title);
         }
 
         }
 
-        $finalTagParts['TYPE'] = $linkDetails['type'];
-        $this->lastTypoLinkLD = $LD;
-
-        // Title tag
-        if (empty($title)) {
-            $title = $conf['title'];
-            if ($conf['title.']) {
-                $title = $this->stdWrap($title, $conf['title.']);
-            }
+        // Target attribute
+        if (!empty($target)) {
+            $tagAttributes['target'] = htmlspecialchars($target);
+        } elseif ($JSwindowParams && !in_array($tsfe->xhtmlDoctype, ['xhtml_strict', 'xhtml_11'], true)) {
+            // Create TARGET-attribute only if the right doctype is used
+            $tagAttributes['target'] = 'FEopenLink';
         }
 
         if ($JSwindowParams) {
         }
 
         if ($JSwindowParams) {
-            // Create TARGET-attribute only if the right doctype is used
-            $xhtmlDocType = $tsfe->xhtmlDoctype;
-            if ($xhtmlDocType !== 'xhtml_strict' && $xhtmlDocType !== 'xhtml_11') {
-                $target = ' target="FEopenLink"';
-            } else {
-                $target = '';
-            }
             $onClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($finalTagParts['url'])) . ',\'FEopenLink\',' . GeneralUtility::quoteJSvalue($JSwindowParams) . ');vHWin.focus();return false;';
             $onClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($finalTagParts['url'])) . ',\'FEopenLink\',' . GeneralUtility::quoteJSvalue($JSwindowParams) . ');vHWin.focus();return false;';
-            $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"'
-                . $target
-                . ' onclick="' . htmlspecialchars($onClick) . '"'
-                . ((string)$title !== '' ? ' title="' . htmlspecialchars($title) . '"' : '')
-                . ($linkClass !== '' ? ' class="' . htmlspecialchars($linkClass) . '"' : '')
-                . $finalTagParts['aTagParams']
-                . '>';
-        } else {
-            if ($tsfe->spamProtectEmailAddresses === 'ascii' && $linkDetails['type'] === LinkService::TYPE_EMAIL) {
-                $finalAnchorTag = '<a href="' . $finalTagParts['url'] . '"';
-            } else {
-                $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"';
-            }
-            $finalAnchorTag .= ((string)$title !== '' ? ' title="' . htmlspecialchars($title) . '"' : '')
-                . $finalTagParts['targetParams']
-                . ($linkClass ? ' class="' . htmlspecialchars($linkClass) . '"' : '')
-                . $finalTagParts['aTagParams']
-                . '>';
+            $tagAttributes['onclick'] = htmlspecialchars($onClick);
+        }
+
+        if (!empty($resolvedLinkParameters['class'])) {
+            $tagAttributes['class'] = htmlspecialchars($resolvedLinkParameters['class']);
+        }
+
+        // Prevent trouble with double and missing spaces between attributes and merge params before implode
+        $finalTagAttributes = array_merge($tagAttributes, GeneralUtility::get_tag_attributes($finalTagParts['aTagParams']));
+        $finalAnchorTag = '<a ' . GeneralUtility::implodeAttributes($finalTagAttributes) . '>';
+
+        if (!empty($finalTagParts['aTagParams'])) {
+            $tagAttributes = array_merge($tagAttributes, GeneralUtility::get_tag_attributes($finalTagParts['aTagParams']));
         }
         }
+        // kept for backwards-compatibility in hooks
+        $finalTagParts['targetParams'] = !empty($tagAttributes['target']) ? ' target="' . $tagAttributes['target'] . '"' : '';
+        $this->lastTypoLinkTarget = $target;
 
         // Call user function:
 
         // Call user function:
-        if ($conf['userFunc']) {
+        if ($conf['userFunc'] ?? false) {
             $finalTagParts['TAG'] = $finalAnchorTag;
             $finalAnchorTag = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $finalTagParts);
         }
 
         // Hook: Call post processing function for link rendering:
             $finalTagParts['TAG'] = $finalAnchorTag;
             $finalAnchorTag = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $finalTagParts);
         }
 
         // 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
-            ];
-            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:
         }
 
         // If flag "returnLastTypoLinkUrl" set, then just return the latest URL made:
-        if ($conf['returnLast']) {
+        if ($conf['returnLast'] ?? false) {
             switch ($conf['returnLast']) {
                 case 'url':
                     return $this->lastTypoLinkUrl;
             switch ($conf['returnLast']) {
                 case 'url':
                     return $this->lastTypoLinkUrl;
@@ -6218,75 +5491,17 @@ class ContentObjectRenderer
             }
         }
 
             }
         }
 
-        $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
+        $wrap = isset($conf['wrap.'])
+            ? $this->stdWrap($conf['wrap'] ?? '', $conf['wrap.'])
+            : $conf['wrap'] ?? '';
 
 
-        if ($conf['ATagBeforeWrap']) {
+        if ($conf['ATagBeforeWrap'] ?? false) {
             return $finalAnchorTag . $this->wrap($linkText, $wrap) . '</a>';
         }
         return $this->wrap($finalAnchorTag . $linkText . '</a>', $wrap);
     }
 
     /**
             return $finalAnchorTag . $this->wrap($linkText, $wrap) . '</a>';
         }
         return $this->wrap($finalAnchorTag . $linkText . '</a>', $wrap);
     }
 
     /**
-     * Helper method to a fallback method parsing HTML out of it
-     *
-     * @param string $originalLinkText the original string, if empty, the fallback link text
-     * @param string $fallbackLinkText the string to be used.
-     * @return string the final text
-     */
-    protected function parseFallbackLinkTextIfLinkTextIsEmpty($originalLinkText, $fallbackLinkText)
-    {
-        if ($originalLinkText === '') {
-            return $this->parseFunc($fallbackLinkText, ['makelinks' => 0], '< lib.parseFunc');
-        } else {
-            return $originalLinkText;
-        }
-    }
-
-    /**
-     * Forces a given URL to be absolute.
-     *
-     * @param string $url The URL to be forced to be absolute
-     * @param array $configuration TypoScript configuration of typolink
-     * @return string The absolute URL
-     */
-    protected function forceAbsoluteUrl($url, array $configuration)
-    {
-        if (!empty($url) && !empty($configuration['forceAbsoluteUrl']) &&  preg_match('#^(?:([a-z]+)(://)([^/]*)/?)?(.*)$#', $url, $matches)) {
-            $urlParts = [
-                'scheme' => $matches[1],
-                'delimiter' => '://',
-                'host' => $matches[3],
-                'path' => $matches[4]
-            ];
-            $isUrlModified = false;
-            // Set scheme and host if not yet part of the URL:
-            if (empty($urlParts['host'])) {
-                $urlParts['scheme'] = $this->getEnvironmentVariable('TYPO3_SSL') ? 'https' : 'http';
-                $urlParts['host'] = $this->getEnvironmentVariable('HTTP_HOST');
-                $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
-                // absRefPrefix has been prepended to $url beforehand
-                // so we only modify the path if no absRefPrefix has been set
-                // otherwise we would destroy the path
-                if ($this->getTypoScriptFrontendController()->absRefPrefix === '') {
-                    $urlParts['path'] = $this->getEnvironmentVariable('TYPO3_SITE_PATH') . ltrim($urlParts['path'], '/');
-                }
-                $isUrlModified = true;
-            }
-            // Override scheme:
-            $forceAbsoluteUrl = &$configuration['forceAbsoluteUrl.']['scheme'];
-            if (!empty($forceAbsoluteUrl) && $urlParts['scheme'] !== $forceAbsoluteUrl) {
-                $urlParts['scheme'] = $forceAbsoluteUrl;
-                $isUrlModified = true;
-            }
-            // Recreate the absolute URL:
-            if ($isUrlModified) {
-                $url = implode('', $urlParts);
-            }
-        }
-        return $url;
-    }
-
-    /**
      * Based on the input "TypoLink" TypoScript configuration this will return the generated URL
      *
      * @param array $conf TypoScript properties for "typolink
      * Based on the input "TypoLink" TypoScript configuration this will return the generated URL
      *
      * @param array $conf TypoScript properties for "typolink
@@ -6404,19 +5619,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.
      * @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 = [])
     {
      * @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;
         }
 
             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);
         foreach ($urlProcessors as $identifier => $configuration) {
             if (empty($configuration) || !is_array($configuration)) {
                 throw new \RuntimeException('Missing configuration for URI processor "' . $identifier . '".', 1442050529);
@@ -6442,70 +5654,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.
      * 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)
     {
      */
     public function getMailTo($mailAddress, $linktxt)
     {
@@ -6517,20 +5672,16 @@ class ContentObjectRenderer
         $originalMailToUrl = 'mailto:' . $mailAddress;
         $mailToUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_MAIL, $originalMailToUrl);
 
         $originalMailToUrl = 'mailto:' . $mailAddress;
         $mailToUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_MAIL, $originalMailToUrl);
 
-        $tsfe = $this->getTypoScriptFrontendController();
-        // no processing happened, therefore
+        // no processing happened, therefore, the default processing kicks in
         if ($mailToUrl === $originalMailToUrl) {
         if ($mailToUrl === $originalMailToUrl) {
+            $tsfe = $this->getTypoScriptFrontendController();
             if ($tsfe->spamProtectEmailAddresses) {
             if ($tsfe->spamProtectEmailAddresses) {
-                if ($tsfe->spamProtectEmailAddresses === 'ascii') {
-                    $mailToUrl = $tsfe->encryptEmail($mailToUrl);
-                } else {
-                    $mailToUrl = 'javascript:linkTo_UnCryptMailto(' . GeneralUtility::quoteJSvalue($tsfe->encryptEmail($mailToUrl)) . ');';
-                }
-                $atLabel = '';
-                if ($tsfe->config['config']['spamProtectEmailAddresses_atSubst']) {
-                    $atLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_atSubst']);
+                $mailToUrl = $this->encryptEmail($mailToUrl, $tsfe->spamProtectEmailAddresses);
+                if ($tsfe->spamProtectEmailAddresses !== 'ascii') {
+                    $mailToUrl = 'javascript:linkTo_UnCryptMailto(' . GeneralUtility::quoteJSvalue($mailToUrl) . ');';
                 }
                 }
-                $spamProtectedMailAddress = str_replace('@', $atLabel ? $atLabel : '(at)', htmlspecialchars($mailAddress));
+                $atLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_atSubst']) ?: '(at)';
+                $spamProtectedMailAddress = str_replace('@', $atLabel, htmlspecialchars($mailAddress));
                 if ($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']) {
                     $lastDotLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']);
                     $lastDotLabel = $lastDotLabel ? $lastDotLabel : '(dot)';
                 if ($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']) {
                     $lastDotLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']);
                     $lastDotLabel = $lastDotLabel ? $lastDotLabel : '(dot)';
@@ -6544,17 +5695,117 @@ class ContentObjectRenderer
     }
 
     /**
     }
 
     /**
+     * Encryption of email addresses for <A>-tags See the spam protection setup in TS 'config.'
+     *
+     * @param string $string Input string to en/decode: "mailto:blabla@bla.com
+     * @param mixed  $type - either "ascii" or a number between -10 and 10, taken from config.spamProtectEmailAddresses
+     * @return string encoded version of $string
+     */
+    protected function encryptEmail($string, $type)
+    {
+        $out = '';
+        // obfuscates using the decimal HTML entity references for each character
+        if ($type === 'ascii') {
+            $stringLength = strlen($string);
+            for ($a = 0; $a < $stringLength; $a++) {
+                $out .= '&#' . ord(substr($string, $a, 1)) . ';';
+            }
+        } else {
+            // like str_rot13() but with a variable offset and a wider character range
+            $len = strlen($string);
+            $offset = (int)$type;
+            for ($i = 0; $i < $len; $i++) {
+                $charValue = ord($string[$i]);
+                // 0-9 . , - + / :
+                if ($charValue >= 43 && $charValue <= 58) {
+                    $out .= $this->encryptCharcode($charValue, 43, 58, $offset);
+                } elseif ($charValue >= 64 && $charValue <= 90) {
+                    // A-Z @
+                    $out .= $this->encryptCharcode($charValue, 64, 90, $offset);
+                } elseif ($charValue >= 97 && $charValue <= 122) {
+                    // a-z
+                    $out .= $this->encryptCharcode($charValue, 97, 122, $offset);
+                } else {
+                    $out .= $string[$i];
+                }
+            }
+        }
+        return $out;
+    }
+
+    /**
+     * Decryption of email addresses for <A>-tags See the spam protection setup in TS 'config.'
+     *
+     * @param string $string Input string to en/decode: "mailto:blabla@bla.com
+     * @param mixed  $type - either "ascii" or a number between -10 and 10 taken from config.spamProtectEmailAddresses
+     * @return string decoded version of $string
+     */
+    protected function decryptEmail($string, $type)
+    {
+        $out = '';
+        // obfuscates using the decimal HTML entity references for each character
+        if ($type === 'ascii') {
+            $stringLength = strlen($string);
+            for ($a = 0; $a < $stringLength; $a++) {
+                $out .= '&#' . ord(substr($string, $a, 1)) . ';';
+            }
+        } else {
+            // like str_rot13() but with a variable offset and a wider character range
+            $len = strlen($string);
+            $offset = (int)$type * -1;
+            for ($i = 0; $i < $len; $i++) {
+                $charValue = ord($string[$i]);
+                // 0-9 . , - + / :
+                if ($charValue >= 43 && $charValue <= 58) {
+                    $out .= $this->encryptCharcode($charValue, 43, 58, $offset);
+                } elseif ($charValue >= 64 && $charValue <= 90) {
+                    // A-Z @
+                    $out .= $this->encryptCharcode($charValue, 64, 90, $offset);
+                } elseif ($charValue >= 97 && $charValue <= 122) {
+                    // a-z
+                    $out .= $this->encryptCharcode($charValue, 97, 122, $offset);
+                } else {
+                    $out .= $string[$i];
+                }
+            }
+        }
+        return $out;
+    }
+
+    /**
+     * Encryption (or decryption) of a single character.
+     * Within the given range the character is shifted with the supplied offset.
+     *
+     * @param int $n Ordinal of input character
+     * @param int $start Start of range
+     * @param int $end End of range
+     * @param int $offset Offset
+     * @return string encoded/decoded version of character
+     */
+    protected function encryptCharcode($n, $start, $end, $offset)
+    {
+        $n = $n + $offset;
+        if ($offset > 0 && $n > $end) {
+            $n = $start + ($n - $end - 1);
+        } elseif ($offset < 0 && $n < $start) {
+            $n = $end - ($start - $n - 1);
+        }
+        return chr($n);
+    }
+
+    /**
      * Gets the query arguments and assembles them for URLs.
      * Arguments may be removed or set, depending on configuration.
      *
      * 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 &)
      */
     public function getQueryArguments($conf, $overruleQueryArguments = [], $forceOverruleArguments = false)
     {
      * @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 &)
      */
     public function getQueryArguments($conf, $overruleQueryArguments = [], $forceOverruleArguments = false)
     {
-        switch ((string)$conf['method']) {
+        $method = (string)($conf['method'] ?? '');
+        switch ($method) {
             case 'GET':
                 $currentQueryArray = GeneralUtility::_GET();
                 break;
             case 'GET':
                 $currentQueryArray = GeneralUtility::_GET();
                 break;
@@ -6570,22 +5821,20 @@ class ContentObjectRenderer
                 ArrayUtility::mergeRecursiveWithOverrule($currentQueryArray, GeneralUtility::_GET());
                 break;
             default:
                 ArrayUtility::mergeRecursiveWithOverrule($currentQueryArray, GeneralUtility::_GET());
                 break;
             default:
-                $currentQueryArray = GeneralUtility::explodeUrl2Array($this->getEnvironmentVariable('QUERY_STRING'), true);
+                $currentQueryArray = [];
+                parse_str($this->getEnvironmentVariable('QUERY_STRING'), $currentQueryArray);
         }
         }
-        if ($conf['exclude']) {
-            $exclude = str_replace(',', '&', $conf['exclude']);
-            $exclude = GeneralUtility::explodeUrl2Array($exclude, true);
+        if ($conf['exclude'] ?? false) {
+            $excludeString = str_replace(',', '&', $conf['exclude']);
+            $excludedQueryParts = [];
+            parse_str($excludeString, $excludedQueryParts);
             // never repeat id
             $exclude['id'] = 0;
             // never repeat id
             $exclude['id'] = 0;
-            $newQueryArray = ArrayUtility::arrayDiffAssocRecursive($currentQueryArray, $exclude);
+            $newQueryArray = ArrayUtility::arrayDiffAssocRecursive($currentQueryArray, $excludedQueryParts);
         } else {
             $newQueryArray = $currentQueryArray;
         }
         } 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);
     }
 
         return GeneralUtility::implodeArrayForUrl('', $newQueryArray, '', false, true);
     }
 
@@ -6609,7 +5858,7 @@ class ContentObjectRenderer
     {
         if ($wrap) {
             $wrapArr = explode($char, $wrap);
     {
         if ($wrap) {
             $wrapArr = explode($char, $wrap);
-            $content = trim($wrapArr[0]) . $content . trim($wrapArr[1]);
+            $content = trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
         }
         return $content;
     }
         }
         return $content;
     }
@@ -6636,39 +5885,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).
      *
      * 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).
      *
@@ -6710,29 +5926,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
      * Cleans up a string of keywords. Keywords at splitted by "," (comma)  ";" (semi colon) and linebreak
      *
      * @param string $content String of keywords
@@ -6757,22 +5950,27 @@ class ContentObjectRenderer
      */
     public function caseshift($theValue, $case)
     {
      */
     public function caseshift($theValue, $case)
     {
-        $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
         switch (strtolower($case)) {
             case 'upper':
         switch (strtolower($case)) {
             case 'upper':
-                $theValue = $charsetConverter->conv_case('utf-8', $theValue, 'toUpper');
+                $theValue = mb_strtoupper($theValue, 'utf-8');
                 break;
             case 'lower':
                 break;
             case 'lower':
-                $theValue = $charsetConverter->conv_case('utf-8', $theValue, 'toLower');
+                $theValue = mb_strtolower($theValue, 'utf-8');
                 break;
             case 'capitalize':
                 break;
             case 'capitalize':
-                $theValue = $charsetConverter->convCapitalize('utf-8', $theValue);
+                $theValue = mb_convert_case($theValue, MB_CASE_TITLE, 'utf-8');
                 break;
             case 'ucfirst':
                 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':
                 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);
                 break;
             case 'uppercamelcase':
                 $theValue = GeneralUtility::underscoredToUpperCamelCase($theValue);
@@ -6844,7 +6042,7 @@ class ContentObjectRenderer
             $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
         } else {
             $val = round($absSeconds / (365 * 24 * 3600));
             $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
         } else {
             $val = round($absSeconds / (365 * 24 * 3600));
-            $seconds = $sign * $val . ($val == 1 ? $labelArr[7] : $labelArr[3]);
+            $seconds = $sign * $val . ($val == 1 ? ($labelArr[7] ?? null) : ($labelArr[3] ?? null));
         }
         return $seconds;
     }
         }
         return $seconds;
     }
@@ -6867,13 +6065,10 @@ class ContentObjectRenderer
         $senderName = trim($senderName);
         $senderAddress = trim($senderAddress);
         if ($senderName !== '' && $senderAddress !== '') {
         $senderName = trim($senderName);
         $senderAddress = trim($senderAddress);
         if ($senderName !== '' && $senderAddress !== '') {
-            $sender = [$senderAddress => $senderName];
+            $mail->setFrom([$senderAddress => $senderName]);
         } elseif ($senderAddress !== '') {
         } elseif ($senderAddress !== '') {
-            $sender = [$senderAddress];
-        } else {
-            $sender = MailUtility::getSystemFrom();
+            $mail->setFrom([$senderAddress]);
         }
         }
-        $mail->setFrom($sender);
         $parsedReplyTo = MailUtility::parseAddresses($replyTo);
         if (!empty($parsedReplyTo)) {
             $mail->setReplyTo($parsedReplyTo);
         $parsedReplyTo = MailUtility::parseAddresses($replyTo);
         if (!empty($parsedReplyTo)) {
             $mail->setReplyTo($parsedReplyTo);
@@ -6893,12 +6088,13 @@ class ContentObjectRenderer
             }
             $parsedCc = MailUtility::parseAddresses($cc);
             if (!empty($parsedCc)) {
             }
             $parsedCc = MailUtility::parseAddresses($cc);
             if (!empty($parsedCc)) {
+                $from = $mail->getFrom();
                 /** @var $mail MailMessage */
                 $mail = GeneralUtility::makeInstance(MailMessage::class);
                 if (!empty($parsedReplyTo)) {
                     $mail->setReplyTo($parsedReplyTo);
                 }
                 /** @var $mail MailMessage */
                 $mail = GeneralUtility::makeInstance(MailMessage::class);
                 if (!empty($parsedReplyTo)) {
                     $mail->setReplyTo($parsedReplyTo);
                 }
-                $mail->setFrom($sender)
+                $mail->setFrom($from)
                     ->setTo($parsedCc)
                     ->setSubject($subject)
                     ->setBody($plainMessage);
                     ->setTo($parsedCc)
                     ->setSubject($subject)
                     ->setBody($plainMessage);
@@ -6910,41 +6106,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
-     */
-    public function URLqMark($url, $params)
-    {
-        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()
-     */
-    public function clearTSProperties($TSArr, $propList)
-    {
-        $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.
      *
      * 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.
      *
@@ -6968,104 +6129,6 @@ class ContentObjectRenderer
         return $confArr;
     }
 
         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
-     */
-    public function gifBuilderTextBox($gifbuilderConf, $conf, $text)
-    {
-        $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()
-     */
-    public function linebreaks($string, $chars, $maxLines = 0)
-    {
-        $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
     /***********************************************
      *
      * Database functions, making of queries
@@ -7086,12 +6149,12 @@ class ContentObjectRenderer
      * @param bool $show_hidden If set, then you want NOT to filter out hidden records. Otherwise hidden record are filtered based on the current preview settings.
      * @param array $ignore_array Array you can pass where keys can be "disabled", "starttime", "endtime", "fe_group" (keys from "enablefields" in TCA) and if set they will make sure that part of the clause is not added. Thus disables the specific part of the clause. For previewing etc.
      * @return string The part of the where clause on the form " AND [fieldname]=0 AND ...". Eg. " AND hidden=0 AND starttime < 123345567
      * @param bool $show_hidden If set, then you want NOT to filter out hidden records. Otherwise hidden record are filtered based on the current preview settings.
      * @param array $ignore_array Array you can pass where keys can be "disabled", "starttime", "endtime", "fe_group" (keys from "enablefields" in TCA) and if set they will make sure that part of the clause is not added. Thus disables the specific part of the clause. For previewing etc.
      * @return string The part of the where clause on the form " AND [fieldname]=0 AND ...". Eg. " AND hidden=0 AND starttime < 123345567
+     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
      */
     public function enableFields($table, $show_hidden = false, array $ignore_array = [])
     {
      */
     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);
+        trigger_error('cObj->enableFields() will be removed in TYPO3 v10. should be used from the PageRepository->enableFields() functionality directly.', E_USER_DEPRECATED);
+        return $this->getTypoScriptFrontendController()->sys_page->enableFields($table, $show_hidden ? true : -1, $ignore_array);
     }
 
     /**
     }
 
     /**
@@ -7148,7 +6211,7 @@ class ContentObjectRenderer
                 $addSelectFields,
                 $moreWhereClauses,
                 $prevId_array,
                 $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)
             ];
             $requestHash = md5(serialize($parameters));
             $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
@@ -7292,9 +6355,16 @@ class ContentObjectRenderer
                             $theList = array_merge(
                                 GeneralUtility::intExplode(
                                     ',',
                             $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
                                     true
                                 ),
                                 $theList
@@ -7336,23 +6406,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.
      */
      * @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 (!$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 . '.' : '';
 
 
         $prefixTableName = $searchTable ? $searchTable . '.' : '';
 
@@ -7368,10 +6429,7 @@ class ContentObjectRenderer
             $searchWord = $queryBuilder->escapeLikeWildcards($searchWord);
             foreach ($searchFields as $field) {
                 $searchWordConstraint->add(
             $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 . '%'))
                 );
             }
 
                 );
             }
 
@@ -7421,20 +6479,11 @@ class ContentObjectRenderer
             $tsfe->sys_page->versionOL($tableName, $row, true);
 
             // Language overlay:
             $tsfe->sys_page->versionOL($tableName, $row, true);
 
             // Language overlay:
-            if (is_array($row) && $tsfe->sys_language_contentOL) {
-                if ($tableName === 'pages') {
-                    $row = $tsfe->sys_page->getPageOverlay($row);
-                } else {
-                    $row = $tsfe->sys_page->getRecordOverlay(
-                        $tableName,
-                        $row,
-                        $tsfe->sys_language_content,
-                        $tsfe->sys_language_contentOL
-                    );
-                }
+            if (is_array($row)) {
+                $row = $tsfe->sys_page->getLanguageOverlay($tableName, $row);
             }
 
             }
 
-            // Might be unset in the sys_language_contentOL
+            // Might be unset in the language overlay
             if (is_array($row)) {
                 $records[] = $row;
             }
             if (is_array($row)) {
                 $records[] = $row;
             }
@@ -7459,6 +6508,7 @@ class ContentObjectRenderer
     public function getQuery($table, $conf, $returnQueryArray = false)
     {
         // Resolve stdWrap in these properties first
     public function getQuery($table, $conf, $returnQueryArray = false)
     {
         // Resolve stdWrap in these properties first
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
         $properties = [
             'pidInList',
             'uidInList',
         $properties = [
             'pidInList',
             'uidInList',
@@ -7475,12 +6525,15 @@ class ContentObjectRenderer
             'where'
         ];
         foreach ($properties as $property) {
             '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]);
             );
             if ($conf[$property] === '') {
                 unset($conf[$property]);
+            } elseif (in_array($property, ['languageField', 'selectFields', 'join', 'leftJoin', 'rightJoin', 'where'], true)) {
+                $conf[$property] = QueryHelper::quoteDatabaseIdentifiers($connection, $conf[$property]);
             }
             if (isset($conf[$property . '.'])) {
                 // stdWrapping already done, so remove the sub-array
             }
             if (isset($conf[$property . '.'])) {
                 // stdWrapping already done, so remove the sub-array
@@ -7542,7 +6595,7 @@ class ContentObjectRenderer
 
         $queryParts = $this->getQueryConstraints($table, $conf);
 
 
         $queryParts = $this->getQueryConstraints($table, $conf);
 
-        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+        $queryBuilder = $connection->createQueryBuilder();
         // @todo Check against getQueryConstraints, can probably use FrontendRestrictions
         // @todo here and remove enableFields there.
         $queryBuilder->getRestrictions()->removeAll();
         // @todo Check against getQueryConstraints, can probably use FrontendRestrictions
         // @todo here and remove enableFields there.
         $queryBuilder->getRestrictions()->removeAll();
@@ -7677,10 +6730,10 @@ class ContentObjectRenderer
             $knownAliases[$tableReference] = true;
 
             $fromClauses[$tableReference] = $tableSql . $this->getQueryArrayJoinHelper(
             $knownAliases[$tableReference] = true;
 
             $fromClauses[$tableReference] = $tableSql . $this->getQueryArrayJoinHelper(
-                $tableReference,
-                $queryBuilder->getQueryPart('join'),
-                $knownAliases
-            );
+                    $tableReference,
+                    $queryBuilder->getQueryPart('join'),
+                    $knownAliases
+                );
         }
 
         $queryParts['SELECT'] = implode(', ', $queryBuilder->getQueryPart('select'));
         }
 
         $queryParts['SELECT'] = implode(', ', $queryBuilder->getQueryPart('select'));
@@ -7755,8 +6808,9 @@ class ContentObjectRenderer
             'orderBy' => null,
         ];
 
             'orderBy' => null,
         ];
 
+        $isInWorkspace = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('workspace', 'isOffline');
         $considerMovePlaceholders = (
         $considerMovePlaceholders = (
-            $tsfe->sys_page->versioningPreview && $table !== 'pages'
+            $isInWorkspace && $table !== 'pages'
             && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
         );
 
             && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
         );
 
@@ -7828,28 +6882,28 @@ class ContentObjectRenderer
 
         if (!empty($languageField)) {
             // The sys_language record UID of the content of the page
 
         if (!empty($languageField)) {
             // The sys_language record UID of the content of the page
-            $sys_language_content = (int)$tsfe->sys_language_content;
+            /** @var LanguageAspect $languageAspect */
+            $languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
 
 
-            if ($tsfe->sys_language_contentOL && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
+            if ($languageAspect->doOverlays() && !empty($GLOBALS['TCA'][$table]['ctrl']['transO