diff --git a/typo3/sysext/core/Classes/Service/MarkerBasedTemplateService.php b/typo3/sysext/core/Classes/Service/MarkerBasedTemplateService.php
index c4f4e9f5d6e05579ab194d2c5e13e6913c6588fc..b8979776be255c58630af355b6aec3d3fd74ea57 100644
--- a/typo3/sysext/core/Classes/Service/MarkerBasedTemplateService.php
+++ b/typo3/sysext/core/Classes/Service/MarkerBasedTemplateService.php
@@ -13,7 +13,9 @@ namespace TYPO3\CMS\Core\Service;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
 
 /**
  * Helper functionality for subparts and marker substitution
@@ -307,4 +309,210 @@ class MarkerBasedTemplateService
 
         return $result;
     }
+
+    /**
+     * 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)
+    {
+        $runtimeCache = $this->getRuntimeCache();
+        // 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)) {
+            return $content;
+        }
+        asort($keysToReplace);
+        $storeKey = md5('substituteMarkerArrayCached_storeKey:' . serialize([$content, $keysToReplace]));
+        if ($runtimeCache->get($storeKey)) {
+            $storeArr = $runtimeCache->get($storeKey);
+        } else {
+            $cache = $this->getCache();
+            $storeArrDat = $cache->get($storeKey);
+            if (is_array($storeArrDat)) {
+                $storeArr = $storeArrDat;
+                // Setting the data in the first level cache
+                $runtimeCache->set($storeKey, $storeArr);
+            } 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 the data inside the second-level cache
+                    $runtimeCache->set($storeKey, $storeArr);
+                    // Storing the cached data permanently
+                    $cache->set($storeKey, $storeArr, ['substMarkArrayCached'], 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'])];
+        }
+        return $content;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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 $htmlSpecialCharsValue If set, all values are passed through htmlspecialchars() - RECOMMENDED to avoid most obvious XSS and maintain XHTML compliance.
+     * @param bool $respectXhtml if set, and $nl2br is set, then the new lines are added with <br /> instead of <br>
+     * @return array The modified $markContentArray
+     */
+    public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = true, $prefix = 'FIELD_', $htmlSpecialCharsValue = false, $respectXhtml = false)
+    {
+        if ($fieldList) {
+            $fArr = GeneralUtility::trimExplode(',', $fieldList, true);
+            foreach ($fArr as $field) {
+                $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($row[$field], $respectXhtml) : $row[$field];
+            }
+        } else {
+            if (is_array($row)) {
+                foreach ($row as $field => $value) {
+                    if (!MathUtility::canBeInterpretedAsInteger($field)) {
+                        if ($htmlSpecialCharsValue) {
+                            $value = htmlspecialchars($value);
+                        }
+                        $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($value, $respectXhtml) : $value;
+                    }
+                }
+            }
+        }
+        return $markContentArray;
+    }
+
+    /**
+     * Second-level cache
+     *
+     * @return \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
+     */
+    protected function getCache()
+    {
+        return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
+    }
+
+    /**
+     * First-level cache (runtime cache)
+     *
+     * @return \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
+     */
+    protected function getRuntimeCache()
+    {
+        return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
+    }
 }
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-80527-Marker-relatedMethodsInContentObjectRenderer.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-80527-Marker-relatedMethodsInContentObjectRenderer.rst
new file mode 100644
index 0000000000000000000000000000000000000000..eb940986e0caa9a65854db87e17f59c2396f21bb
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-80527-Marker-relatedMethodsInContentObjectRenderer.rst
@@ -0,0 +1,43 @@
+.. include:: ../../Includes.txt
+
+=====================================================================
+Deprecation: #80527 - Marker-related methods in ContentObjectRenderer
+=====================================================================
+
+See :issue:`80527`
+
+Description
+===========
+
+The following methods within php:`ContentObjectRenderer` PHP class have been marked as deprecated:
+
+* getSubpart()
+* substituteSubpart()
+* substituteSubpartArray()
+* substituteMarker()
+* substituteMarkerArrayCached()
+* substituteMarkerArray()
+* substituteMarkerInObject()
+* substituteMarkerAndSubpartArrayRecursive()
+* fillInMarkerArray()
+
+
+Impact
+======
+
+Calling any of the methods above will trigger a deprecation log entry.
+
+
+Affected Installations
+======================
+
+Any installation using custom extensions calling these API methods.
+
+
+Migration
+=========
+
+Instantiate the class `MarkerBasedTemplateService` available in TYPO3 v7, which contains equivalents
+to all methods that have been marked as deprecated with the same functionality and namings.
+
+.. index:: PHP-API
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Unit/Service/MarkerBasedTemplateServiceTest.php b/typo3/sysext/core/Tests/Unit/Service/MarkerBasedTemplateServiceTest.php
index 1cd4496905cfacb8f8e8ca9f38725975df106da0..032c0e3bd3a04d6f9af8a4103bd7f49221879ff1 100644
--- a/typo3/sysext/core/Tests/Unit/Service/MarkerBasedTemplateServiceTest.php
+++ b/typo3/sysext/core/Tests/Unit/Service/MarkerBasedTemplateServiceTest.php
@@ -14,6 +14,10 @@ namespace TYPO3\CMS\Core\Tests\Unit\Service;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Prophecy\Argument;
+use Prophecy\Prophecy\ObjectProphecy;
+use TYPO3\CMS\Core\Cache\CacheManager;
+use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -27,9 +31,29 @@ class MarkerBasedTemplateServiceTest extends \TYPO3\TestingFramework\Core\Unit\U
      */
     protected $templateService;
 
+    /**
+     * @var array A backup of registered singleton instances
+     */
+    protected $singletonInstances = [];
+
     protected function setUp()
     {
-        $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
+        $this->singletonInstances = GeneralUtility::getSingletonInstances();
+
+        /** @var CacheManager|ObjectProphecy $cacheManagerProphecy */
+        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
+        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
+        $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
+        $cacheManagerProphecy->getCache(Argument::cetera())->willReturn($cacheFrontendProphecy->reveal());
+
+        $this->templateService = new MarkerBasedTemplateService();
+    }
+
+    protected function tearDown()
+    {
+        GeneralUtility::purgeInstances();
+        GeneralUtility::resetSingletonInstances($this->singletonInstances);
+        parent::tearDown();
     }
 
     /**
@@ -605,4 +629,121 @@ Value 2.2
     {
         $this->assertSame($expected, $this->templateService->substituteMarkerAndSubpartArrayRecursive($template, $markersAndSubparts, $wrap, $uppercase, $deleteUnused));
     }
+
+    /**
+     * @return array
+     */
+    public function substituteMarkerArrayCachedReturnsExpectedContentDataProvider()
+    {
+        return [
+            'no markers defined' => [
+                'dummy content with ###UNREPLACED### marker',
+                [],
+                [],
+                [],
+                'dummy content with ###UNREPLACED### marker',
+            ],
+            'no markers used' => [
+                'dummy content with no marker',
+                [
+                    '###REPLACED###' => '_replaced_'
+                ],
+                [],
+                [],
+                'dummy content with no marker',
+            ],
+            'one marker' => [
+                'dummy content with ###REPLACED### marker',
+                [
+                    '###REPLACED###' => '_replaced_'
+                ],
+                [],
+                [],
+                'dummy content with _replaced_ marker'
+            ],
+            'one marker with lots of chars' => [
+                'dummy content with ###RE.:##-=_()LACED### marker',
+                [
+                    '###RE.:##-=_()LACED###' => '_replaced_'
+                ],
+                [],
+                [],
+                'dummy content with _replaced_ marker'
+            ],
+            'markers which are special' => [
+                'dummy ###aa##.#######A### ######',
+                [
+                    '###aa##.###' => 'content ',
+                    '###A###' => 'is',
+                    '######' => '-is not considered-'
+                ],
+                [],
+                [],
+                'dummy content #is ######'
+            ],
+            'two markers in content, but more defined' => [
+                'dummy ###CONTENT### with ###REPLACED### marker',
+                [
+                    '###REPLACED###' => '_replaced_',
+                    '###CONTENT###' => 'content',
+                    '###NEVERUSED###' => 'bar'
+                ],
+                [],
+                [],
+                'dummy content with _replaced_ marker'
+            ],
+            'one subpart' => [
+                'dummy content with ###ASUBPART### around some text###ASUBPART###.',
+                [],
+                [
+                    '###ASUBPART###' => 'some other text'
+                ],
+                [],
+                'dummy content with some other text.'
+            ],
+            'one wrapped subpart' => [
+                'dummy content with ###AWRAPPEDSUBPART### around some text###AWRAPPEDSUBPART###.',
+                [],
+                [],
+                [
+                    '###AWRAPPEDSUBPART###' => [
+                        'more content',
+                        'content'
+                    ]
+                ],
+                'dummy content with more content around some textcontent.'
+            ],
+            'one subpart with markers, not replaced recursively' => [
+                'dummy ###CONTENT### with ###ASUBPART### around ###SOME### text###ASUBPART###.',
+                [
+                    '###CONTENT###' => 'content',
+                    '###SOME###' => '-this should never make it into output-',
+                    '###OTHER_NOT_REPLACED###' => '-this should never make it into output-'
+                ],
+                [
+                    '###ASUBPART###' => 'some ###OTHER_NOT_REPLACED### text'
+                ],
+                [],
+                'dummy content with some ###OTHER_NOT_REPLACED### text.'
+            ],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider substituteMarkerArrayCachedReturnsExpectedContentDataProvider
+     *
+     * @param string $content
+     * @param array $markContentArray
+     * @param array $subpartContentArray
+     * @param array $wrappedSubpartContentArray
+     * @param string $expectedContent
+     * @param bool $shouldQueryCache
+     * @param bool $shouldStoreCache
+     */
+    public function substituteMarkerArrayCachedReturnsExpectedContent($content, array $markContentArray, array $subpartContentArray, array $wrappedSubpartContentArray, $expectedContent)
+    {
+        $resultContent = $this->templateService->substituteMarkerArrayCached($content, $markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
+        $this->assertSame($expectedContent, $resultContent);
+    }
 }
diff --git a/typo3/sysext/css_styled_content/Classes/Controller/CssStyledContentController.php b/typo3/sysext/css_styled_content/Classes/Controller/CssStyledContentController.php
index c9665597a94eaa3b1db911cd5426eee6661747c2..7f4fc5deaa6eaf9f91f4077155127a78201d0b94 100644
--- a/typo3/sysext/css_styled_content/Classes/Controller/CssStyledContentController.php
+++ b/typo3/sysext/css_styled_content/Classes/Controller/CssStyledContentController.php
@@ -614,13 +614,13 @@ class CssStyledContentController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlug
                             // Get the caption
                             if (!$renderGlobalCaption) {
                                 $imageMarkers['caption'] = $this->cObj->stdWrap($this->cObj->cObjGet($conf['caption.'], 'caption.'), $conf['caption.']);
-                                $imageMarkers['caption'] = $this->cObj->substituteMarkerArray($imageMarkers['caption'], $captionMarkers, '###|###', 1, 1);
+                                $imageMarkers['caption'] = $this->templateService->substituteMarkerArray($imageMarkers['caption'], $captionMarkers, '###|###', 1, 1);
                             }
                             if ($addClassesImageConf[$imagesCounter - 1]['addClassesImage']) {
                                 $imageMarkers['classes'] = ' ' . $addClassesImageConf[$imagesCounter - 1]['addClassesImage'];
                             }
                         }
-                        $columnImages[] = $this->cObj->substituteMarkerArray($single, $imageMarkers, '###|###', 1, 1);
+                        $columnImages[] = $this->templateService->substituteMarkerArray($single, $imageMarkers, '###|###', 1, 1);
                         $currentImage++;
                     }
                     $rowColumn = $this->cObj->stdWrap(implode(LF, $columnImages), $conf['columnStdWrap.']);
@@ -629,7 +629,7 @@ class CssStyledContentController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlug
                     if ($addClassesColConf[$columnCounter - 1]['addClassesCol']) {
                         $columnMarkers['classes'] = ' ' . $addClassesColConf[$columnCounter - 1]['addClassesCol'];
                     }
-                    $rowColumns[] = $this->cObj->substituteMarkerArray($rowColumn, $columnMarkers, '###|###', 1, 1);
+                    $rowColumns[] = $this->templateService->substituteMarkerArray($rowColumn, $columnMarkers, '###|###', 1, 1);
                 }
                 if ($rowCounter == $rowCount) {
                     $rowConfiguration = $conf['lastRowStdWrap.'];
@@ -639,7 +639,7 @@ class CssStyledContentController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlug
                 $row = $this->cObj->stdWrap(implode(LF, $rowColumns), $rowConfiguration);
                 // Start filling the markers for columnStdWrap
                 $rowMarkers = [];
-                $rows[] = $this->cObj->substituteMarkerArray($row, $rowMarkers, '###|###', 1, 1);
+                $rows[] = $this->templateService->substituteMarkerArray($row, $rowMarkers, '###|###', 1, 1);
             }
             $images = $this->cObj->stdWrap(implode(LF, $rows), $conf['allStdWrap.']);
             // Start filling the markers for allStdWrap
@@ -681,7 +681,7 @@ class CssStyledContentController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlug
                 $class = ' ' . implode(' ', $classes);
             }
             // Fill the markers for the allStdWrap
-            $images = $this->cObj->substituteMarkerArray($images, $allMarkers, '###|###', 1, 1);
+            $images = $this->templateService->substituteMarkerArray($images, $allMarkers, '###|###', 1, 1);
         } else {
             // Apply optionSplit to the list of classes that we want to add to each image
             $addClassesImage = $conf['addClassesImage'];
diff --git a/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php b/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php
index 9a8bdaecb9b21c2f8e00d248ba4b0de0d48cea82..925ad45aeead6cf9030a521d687dfd067100b0c6 100644
--- a/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php
+++ b/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php
@@ -215,7 +215,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
      */
     protected function showForgot()
     {
-        $subpart = $this->cObj->getSubpart($this->template, '###TEMPLATE_FORGOT###');
+        $subpart = $this->templateService->getSubpart($this->template, '###TEMPLATE_FORGOT###');
         $subpartArray = ($linkpartArray = []);
         $postData = GeneralUtility::_POST($this->prefixId);
         if ($postData['forgot_email']) {
@@ -292,7 +292,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
         $markerArray['###FORGOTHASH###'] = $hash;
         // Set hash in feuser session
         $this->frontendController->fe_user->setKey('ses', 'forgot_hash', ['forgot_hash' => $hash]);
-        return $this->cObj->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
+        return $this->templateService->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
     }
 
     /**
@@ -306,7 +306,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
         $subpartArray = ($linkpartArray = []);
         $done = false;
         $minLength = (int)$this->conf['newPasswordMinLength'] ?: 6;
-        $subpart = $this->cObj->getSubpart($this->template, '###TEMPLATE_CHANGEPASSWORD###');
+        $subpart = $this->templateService->getSubpart($this->template, '###TEMPLATE_CHANGEPASSWORD###');
         $markerArray['###STATUS_HEADER###'] = $this->getDisplayText('change_password_header', $this->conf['changePasswordHeader_stdWrap.']);
         $markerArray['###STATUS_MESSAGE###'] = sprintf($this->getDisplayText(
             'change_password_message',
@@ -410,7 +410,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
                 }
             }
         }
-        return $this->cObj->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
+        return $this->templateService->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
     }
 
     /**
@@ -501,7 +501,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
      */
     protected function showLogout()
     {
-        $subpart = $this->cObj->getSubpart($this->template, '###TEMPLATE_LOGOUT###');
+        $subpart = $this->templateService->getSubpart($this->template, '###TEMPLATE_LOGOUT###');
         $subpartArray = ($linkpartArray = []);
         $markerArray['###STATUS_HEADER###'] = $this->getDisplayText('status_header', $this->conf['logoutHeader_stdWrap.']);
         $markerArray['###STATUS_MESSAGE###'] = $this->getDisplayText('status_message', $this->conf['logoutMessage_stdWrap.']);
@@ -521,7 +521,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
             $markerArray['###ACTION_URI###'] = htmlspecialchars($this->redirectUrl);
             $this->redirectUrl = '';
         }
-        return $this->cObj->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
+        return $this->templateService->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
     }
 
     /**
@@ -531,7 +531,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
      */
     protected function showLogin()
     {
-        $subpart = $this->cObj->getSubpart($this->template, '###TEMPLATE_LOGIN###');
+        $subpart = $this->templateService->getSubpart($this->template, '###TEMPLATE_LOGIN###');
         $subpartArray = ($linkpartArray = ($markerArray = []));
         $gpRedirectUrl = '';
         $markerArray['###LEGEND###'] = htmlspecialchars($this->pi_getLL('oLabel_header_welcome'));
@@ -659,7 +659,7 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
         } else {
             $subpartArray['###PERMALOGIN_VALID###'] = '';
         }
-        return $this->cObj->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
+        return $this->templateService->substituteMarkerArrayCached($subpart, $markerArray, $subpartArray, $linkpartArray);
     }
 
     /**
diff --git a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
index aa49db419e65066e79d6b1aaa6e9a696cb756f03..fdedaec27b856432a1bfbdda62499aee29d58c2a 100644
--- a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
+++ b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
@@ -372,13 +372,6 @@ class ContentObjectRenderer
      */
     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!
      *
@@ -1088,7 +1081,7 @@ class ContentObjectRenderer
             'selfClosingTagSlash' => (!empty($tsfe->xhtmlDoctype) ? ' /' : ''),
         ];
 
-        $theValue = $this->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
+        $theValue = $this->templateService->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
 
         $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
         if ($linkWrap) {
@@ -1227,7 +1220,7 @@ class ContentObjectRenderer
                     $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
                     $sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : '';
 
-                    $oneSourceCollection = $this->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
+                    $oneSourceCollection = $this->templateService->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
 
                     if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'])) {
                         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] as $classData) {
@@ -1551,9 +1544,11 @@ class ContentObjectRenderer
      * @param string $content The content stream, typically HTML template content.
      * @param string $marker The marker string, typically on the form "###[the marker string]###
      * @return string The subpart found, if found.
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function getSubpart($content, $marker)
     {
+        GeneralUtility::logDeprecatedFunction();
         return $this->templateService->getSubpart($content, $marker);
     }
 
@@ -1568,9 +1563,11 @@ class ContentObjectRenderer
      * @param mixed $subpartContent The content to insert instead of the subpart found. If a string, then just plain substitution happens (includes removing the HTML comments of the subpart if found). If $subpartContent happens to be an array, it's [0] and [1] elements are wrapped around the EXISTING content of the subpart (fetched by getSubpart()) thereby not removing the original content.
      * @param bool|int $recursive If $recursive is set, the function calls itself with the content set to the remaining part of the content after the second marker. This means that proceding subparts are ALSO substituted!
      * @return string The processed HTML content string.
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function substituteSubpart($content, $marker, $subpartContent, $recursive = 1)
     {
+        GeneralUtility::logDeprecatedFunction();
         return $this->templateService->substituteSubpart($content, $marker, $subpartContent, $recursive);
     }
 
@@ -1580,9 +1577,11 @@ class ContentObjectRenderer
      * @param string $content The content stream, typically HTML template content.
      * @param array $subpartsContent The array of key/value pairs being subpart/content values used in the substitution. For each element in this array the function will substitute a subpart in the content stream with the content.
      * @return string The processed HTML content string.
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function substituteSubpartArray($content, array $subpartsContent)
     {
+        GeneralUtility::logDeprecatedFunction();
         return $this->templateService->substituteSubpartArray($content, $subpartsContent);
     }
 
@@ -1595,9 +1594,11 @@ class ContentObjectRenderer
      * @param mixed $markContent The content to insert instead of the marker string found.
      * @return string The processed HTML content string.
      * @see substituteSubpart()
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function substituteMarker($content, $marker, $markContent)
     {
+        GeneralUtility::logDeprecatedFunction();
         return $this->templateService->substituteMarker($content, $marker, $markContent);
     }
 
@@ -1632,110 +1633,12 @@ class ContentObjectRenderer
      * @param array $wrappedSubpartContentArray An array of arrays with 0/1 keys where the subparts pointed to by the main key is wrapped with the 0/1 value alternating.
      * @return string The output content stream
      * @see substituteSubpart(), substituteMarker(), substituteMarkerInObject(), TEMPLATE()
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function substituteMarkerArrayCached($content, array $markContentArray = null, array $subpartContentArray = null, array $wrappedSubpartContentArray = null)
     {
-        $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 {
-            $cache = $this->getCache();
-            $storeArrDat = $cache->get($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
-                    $cache->set($storeKey, $storeArr, ['substMarkArrayCached'], 0);
-                }
-                $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;
+        GeneralUtility::logDeprecatedFunction();
+        return $this->templateService->substituteMarkerArrayCached($content, $markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
     }
 
     /**
@@ -1756,9 +1659,11 @@ class ContentObjectRenderer
      * @param bool $deleteUnused If set, all unused marker are deleted.
      * @return string The processed output stream
      * @see substituteMarker(), substituteMarkerInObject(), TEMPLATE()
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function substituteMarkerArray($content, array $markContentArray, $wrap = '', $uppercase = false, $deleteUnused = false)
     {
+        GeneralUtility::logDeprecatedFunction();
         return $this->templateService->substituteMarkerArray($content, $markContentArray, $wrap, $uppercase, $deleteUnused);
     }
 
@@ -1769,15 +1674,17 @@ class ContentObjectRenderer
      * @param array $markContentArray The array of key/value pairs being marker/content values used in the substitution. For each element in this array the function will substitute a marker in the content string/array values.
      * @return mixed The processed input variable.
      * @see substituteMarker()
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function substituteMarkerInObject(&$tree, array $markContentArray)
     {
+        GeneralUtility::logDeprecatedFunction();
         if (is_array($tree)) {
             foreach ($tree as $key => $value) {
-                $this->substituteMarkerInObject($tree[$key], $markContentArray);
+                $this->templateService->substituteMarkerInObject($tree[$key], $markContentArray);
             }
         } else {
-            $tree = $this->substituteMarkerArray($tree, $markContentArray);
+            $tree = $this->templateService->substituteMarkerArray($tree, $markContentArray);
         }
         return $tree;
     }
@@ -1791,9 +1698,11 @@ class ContentObjectRenderer
      * @param bool $uppercase
      * @param bool $deleteUnused
      * @return string
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = false, $deleteUnused = false)
     {
+        GeneralUtility::logDeprecatedFunction();
         return $this->templateService->substituteMarkerAndSubpartArrayRecursive($content, $markersAndSubparts, $wrap, $uppercase, $deleteUnused);
     }
 
@@ -1808,28 +1717,13 @@ class ContentObjectRenderer
      * @param string $prefix Prefix string to the fieldname before it is added as a key in the $markContentArray. Notice that the keys added to the $markContentArray always start and end with "###
      * @param bool $HSC If set, all values are passed through htmlspecialchars() - RECOMMENDED to avoid most obvious XSS and maintain XHTML compliance.
      * @return array The modified $markContentArray
+     * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, please use the MarkerBasedTemplateService instead.
      */
     public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = true, $prefix = 'FIELD_', $HSC = false)
     {
+        GeneralUtility::logDeprecatedFunction();
         $tsfe = $this->getTypoScriptFrontendController();
-        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;
+        return $this->templateService->fillInMarkerArray($markContentArray, $row, $fieldList, $nl2br, $prefix, $HSC, !empty($tsfe->xhtmlDoctype));
     }
 
     /**
@@ -8373,12 +8267,4 @@ class ContentObjectRenderer
     {
         return $this->typoScriptFrontendController ?: $GLOBALS['TSFE'];
     }
-
-    /**
-     * @return \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
-     */
-    protected function getCache()
-    {
-        return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
-    }
 }
diff --git a/typo3/sysext/frontend/Classes/ContentObject/TemplateContentObject.php b/typo3/sysext/frontend/Classes/ContentObject/TemplateContentObject.php
index 2a6ccba2c7392a0b4619aeeedc3ba5f9dfe88102..40f2944d794c0c2f97c723552b7b3f19e20a7a5c 100644
--- a/typo3/sysext/frontend/Classes/ContentObject/TemplateContentObject.php
+++ b/typo3/sysext/frontend/Classes/ContentObject/TemplateContentObject.php
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Frontend\ContentObject;
  */
 
 use TYPO3\CMS\Core\Html\HtmlParser;
+use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -22,6 +23,22 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  */
 class TemplateContentObject extends AbstractContentObject
 {
+    /**
+     * @var MarkerBasedTemplateService
+     */
+    protected $templateService;
+
+    /**
+     * Default constructor, which also instantiates the MarkerBasedTemplateService.
+     *
+     * @param ContentObjectRenderer $cObj
+     */
+    public function __construct(ContentObjectRenderer $cObj)
+    {
+        $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
+        parent::__construct($cObj);
+    }
+
     /**
      * Rendering the cObject, TEMPLATE
      *
@@ -45,7 +62,7 @@ class TemplateContentObject extends AbstractContentObject
         $content = $this->cObj->cObjGetSingle($conf['template'], $conf['template.'], 'template');
         $workOnSubpart = isset($conf['workOnSubpart.']) ? $this->cObj->stdWrap($conf['workOnSubpart'], $conf['workOnSubpart.']) : $conf['workOnSubpart'];
         if ($workOnSubpart) {
-            $content = $this->cObj->getSubpart($content, $PRE . $workOnSubpart . $POST);
+            $content = $this->templateService->getSubpart($content, $PRE . $workOnSubpart . $POST);
         }
         // Fixing all relative paths found:
         if ($conf['relPathPrefix']) {
@@ -68,10 +85,10 @@ class TemplateContentObject extends AbstractContentObject
                 if (is_array($conf['subparts.'])) {
                     foreach ($conf['subparts.'] as $theKey => $theValue) {
                         if (!strstr($theKey, '.')) {
-                            $subpart = $this->cObj->getSubpart($content, $PRE . $theKey . $POST);
+                            $subpart = $this->templateService->getSubpart($content, $PRE . $theKey . $POST);
                             if ($subpart) {
                                 $this->cObj->setCurrentVal($subpart);
-                                $content = $this->cObj->substituteSubpart($content, $PRE . $theKey . $POST, $this->cObj->cObjGetSingle($theValue, $conf['subparts.'][$theKey . '.'], 'subparts.' . $theKey), true);
+                                $content = $this->templateService->substituteSubpart($content, $PRE . $theKey . $POST, $this->cObj->cObjGetSingle($theValue, $conf['subparts.'][$theKey . '.'], 'subparts.' . $theKey), true);
                             }
                         }
                     }
@@ -80,10 +97,10 @@ class TemplateContentObject extends AbstractContentObject
                 if (is_array($conf['wraps.'])) {
                     foreach ($conf['wraps.'] as $theKey => $theValue) {
                         if (!strstr($theKey, '.')) {
-                            $subpart = $this->cObj->getSubpart($content, $PRE . $theKey . $POST);
+                            $subpart = $this->templateService->getSubpart($content, $PRE . $theKey . $POST);
                             if ($subpart) {
                                 $this->cObj->setCurrentVal($subpart);
-                                $content = $this->cObj->substituteSubpart($content, $PRE . $theKey . $POST, explode('|', $this->cObj->cObjGetSingle($theValue, $conf['wraps.'][$theKey . '.'], 'wraps.' . $theKey)), true);
+                                $content = $this->templateService->substituteSubpart($content, $PRE . $theKey . $POST, explode('|', $this->cObj->cObjGetSingle($theValue, $conf['wraps.'][$theKey . '.'], 'wraps.' . $theKey)), true);
                             }
                         }
                     }
@@ -94,7 +111,7 @@ class TemplateContentObject extends AbstractContentObject
                 if (is_array($conf['subparts.'])) {
                     foreach ($conf['subparts.'] as $theKey => $theValue) {
                         if (!strstr($theKey, '.')) {
-                            $subpart = $this->cObj->getSubpart($content, $PRE . $theKey . $POST);
+                            $subpart = $this->templateService->getSubpart($content, $PRE . $theKey . $POST);
                             if ($subpart) {
                                 $GLOBALS['TSFE']->register['SUBPART_' . $theKey] = $subpart;
                                 $subparts[$theKey]['name'] = $theValue;
@@ -144,10 +161,10 @@ class TemplateContentObject extends AbstractContentObject
                 // Substitution
                 $substMarksSeparately = isset($conf['substMarksSeparately.']) ? $this->cObj->stdWrap($conf['substMarksSeparately'], $conf['substMarksSeparately.']) : $conf['substMarksSeparately'];
                 if ($substMarksSeparately) {
-                    $content = $this->cObj->substituteMarkerArrayCached($content, [], $subpartArray, $subpartWraps);
-                    $content = $this->cObj->substituteMarkerArray($content, $markerArray);
+                    $content = $this->templateService->substituteMarkerArrayCached($content, [], $subpartArray, $subpartWraps);
+                    $content = $this->templateService->substituteMarkerArray($content, $markerArray);
                 } else {
-                    $content = $this->cObj->substituteMarkerArrayCached($content, $markerArray, $subpartArray, $subpartWraps);
+                    $content = $this->templateService->substituteMarkerArrayCached($content, $markerArray, $subpartArray, $subpartWraps);
                 }
             }
         }
diff --git a/typo3/sysext/frontend/Classes/Plugin/AbstractPlugin.php b/typo3/sysext/frontend/Classes/Plugin/AbstractPlugin.php
index 53661a0074c697ee77d10a1c6f49694f4b1c14b4..b0b50e0b2f9134ae30b719eceeda3187861c8833 100644
--- a/typo3/sysext/frontend/Classes/Plugin/AbstractPlugin.php
+++ b/typo3/sysext/frontend/Classes/Plugin/AbstractPlugin.php
@@ -22,6 +22,7 @@ use TYPO3\CMS\Core\Database\Query\QueryHelper;
 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
 use TYPO3\CMS\Core\Localization\Locales;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
+use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
@@ -234,6 +235,11 @@ class AbstractPlugin
      */
     protected $databaseConnection;
 
+    /**
+     * @var MarkerBasedTemplateService
+     */
+    protected $templateService;
+
     /**
      * Class Constructor (true constructor)
      * Initializes $this->piVars if $this->prefixId is set to any value
@@ -246,6 +252,7 @@ class AbstractPlugin
     {
         $this->databaseConnection = $databaseConnection ?: $GLOBALS['TYPO3_DB'];
         $this->frontendController = $frontendController ?: $GLOBALS['TSFE'];
+        $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
         // Setting piVars:
         if ($this->prefixId) {
             $this->piVars = GeneralUtility::_GPmerged($this->prefixId);
@@ -675,7 +682,7 @@ class AbstractPlugin
                 $markerArray['###CURRENT_PAGE###'] = $this->cObj->wrap($pointer + 1, $wrapper['showResultsNumbersWrap']);
                 $markerArray['###TOTAL_PAGES###'] = $this->cObj->wrap($totalPages, $wrapper['showResultsNumbersWrap']);
                 // Substitute markers
-                $resultCountMsg = $this->cObj->substituteMarkerArray($this->pi_getLL('pi_list_browseresults_displays', 'Displaying results ###FROM### to ###TO### out of ###OUT_OF###'), $markerArray);
+                $resultCountMsg = $this->templateService->substituteMarkerArray($this->pi_getLL('pi_list_browseresults_displays', 'Displaying results ###FROM### to ###TO### out of ###OUT_OF###'), $markerArray);
             } else {
                 // Render the resultcount in the "traditional" way using sprintf
                 $resultCountMsg = sprintf(str_replace('###SPAN_BEGIN###', '<span' . $this->pi_classParam('browsebox-strong') . '>', $this->pi_getLL('pi_list_browseresults_displays', 'Displaying results ###SPAN_BEGIN###%s to %s</span> out of ###SPAN_BEGIN###%s</span>')), $count > 0 ? $pR1 : 0, min($count, $pR2), $count);