[BUGFIX] Remove caches for page title and meta tag 20/60520/12
authorHelmut Hummel <typo3@helhum.io>
Fri, 19 Apr 2019 16:03:31 +0000 (18:03 +0200)
committerRichard Haeser <richard@maxserv.com>
Fri, 21 Jun 2019 13:01:20 +0000 (15:01 +0200)
By concept for frontend rendering the page title and meta tags
are not meant to be stored in page cache in order to allow
non cachable plugins to modify those.

Currently both page title and meta tags are stored
in separate cache entries, which violates the concept above
and unnecessarily tightly couples those code parts to the
TypoScriptFrontendController and internal logic of it.

This patch removes these caches.

In order to properly handle the uncached rendering state,
we make sure the meta tag registry is properly
serialized with all managers.

Resolves: #88179
Releases: master, 9.5
Change-Id: If5200398bf9ab9db09ab97403c976d82cb33d01d
Signed-off-by: Frank Naegler <frank.naegler@typo3.org>
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/60520
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Helmut Hummel <typo3@helhum.io>
Tested-by: Richard Haeser <richard@maxserv.com>
Reviewed-by: Helmut Hummel <typo3@helhum.io>
Reviewed-by: Richard Haeser <richard@maxserv.com>
15 files changed:
typo3/sysext/core/Classes/MetaTag/AbstractMetaTagManager.php
typo3/sysext/core/Classes/MetaTag/GenericMetaTagManager.php
typo3/sysext/core/Classes/MetaTag/MetaTagManagerRegistry.php
typo3/sysext/core/Classes/Page/PageRenderer.php
typo3/sysext/core/Classes/PageTitle/PageTitleProviderManager.php
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/Controller/MetaPluginController.php [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/PageTitle/CustomPageTitleProvider.php [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page1.typoscript [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page2.typoscript [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page3.typoscript [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page4.typoscript [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page5.typoscript [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/ext_emconf.php [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Scenarios/pages_with_plugins_seo_meta.xml [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/MetaDataHandling/PluginsTest.php [new file with mode: 0644]

index 9a09b5c..422c971 100644 (file)
@@ -16,9 +16,7 @@ namespace TYPO3\CMS\Core\MetaTag;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\SingletonInterface;
-
-abstract class AbstractMetaTagManager implements MetaTagManagerInterface, SingletonInterface
+abstract class AbstractMetaTagManager implements MetaTagManagerInterface
 {
     /**
      * The default attribute that defines the name of the property
index b7a03a5..b724bef 100644 (file)
@@ -16,13 +16,11 @@ namespace TYPO3\CMS\Core\MetaTag;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\SingletonInterface;
-
 /**
  * Handles typical meta tags (non-grouped). Use AbstractMetaTagManager
  * to create you own MetaTags, this class is final by design
  */
-final class GenericMetaTagManager implements MetaTagManagerInterface, SingletonInterface
+final class GenericMetaTagManager implements MetaTagManagerInterface
 {
     /**
      * The separator to define subproperties like og:image:width
index b542c60..0ddf9b6 100644 (file)
@@ -26,6 +26,8 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 class MetaTagManagerRegistry implements SingletonInterface
 {
     protected $registry = [];
+    private $instances = [];
+    private $managers;
 
     public function __construct()
     {
@@ -53,6 +55,7 @@ class MetaTagManagerRegistry implements SingletonInterface
             'before' => $before,
             'after' => $after
         ];
+        $this->managers = null;
     }
 
     /**
@@ -81,18 +84,24 @@ class MetaTagManagerRegistry implements SingletonInterface
      */
     public function getAllManagers(): array
     {
+        if ($this->managers !== null) {
+            return $this->managers;
+        }
+
         $orderedManagers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies(
             $this->registry
         );
 
-        $managers = [];
+        $this->managers = [];
         foreach ($orderedManagers as $manager => $managerConfiguration) {
-            if (class_exists($managerConfiguration['module'])) {
-                $managers[$manager] = GeneralUtility::makeInstance($managerConfiguration['module']);
+            $module = $managerConfiguration['module'];
+            if (class_exists($module)) {
+                $this->instances[$module] = $this->instances[$module] ?? GeneralUtility::makeInstance($module);
+                $this->managers[$manager] = $this->instances[$module];
             }
         }
 
-        return $managers;
+        return $this->managers;
     }
 
     /**
@@ -100,6 +109,7 @@ class MetaTagManagerRegistry implements SingletonInterface
      */
     public function removeAllManagers()
     {
-        unset($this->registry);
+        $this->registry = [];
+        $this->managers = null;
     }
 }
index 75dca76..f161375 100644 (file)
@@ -18,7 +18,6 @@ use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Template\DocumentTemplate;
 use TYPO3\CMS\Core\Cache\CacheManager;
-use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
@@ -171,13 +170,6 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
     protected $metaTags = [];
 
     /**
-     * META Tags added via the API
-     *
-     * @var array
-     */
-    protected $metaTagsByAPI = [];
-
-    /**
      * @var array
      */
     protected $inlineComments = [];
@@ -359,6 +351,15 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
     }
 
     /**
+     * Set restored meta tag managers as singletons
+     * so that uncached plugins can use them to add or remove meta tags
+     */
+    public function __wakeup()
+    {
+        GeneralUtility::setSingletonInstance(get_class($this->metaTagRegistry), $this->metaTagRegistry);
+    }
+
+    /**
      * @param FrontendInterface $cache
      */
     public static function setCache(FrontendInterface $cache)
@@ -918,7 +919,6 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
                 1496402460
             );
         }
-
         $manager = $this->metaTagRegistry->getManagerForProperty($name);
         $manager->addProperty($name, $content, $subProperties, $replace, $type);
     }
@@ -1674,33 +1674,11 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
     {
         $metaTags = [];
         $metaTagManagers = $this->metaTagRegistry->getAllManagers();
-        try {
-            $cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('pages');
-        } catch (NoSuchCacheException $e) {
-            $cache = null;
-        }
 
         foreach ($metaTagManagers as $manager => $managerObject) {
-            $cacheIdentifier =  $this->getTypoScriptFrontendController()->newHash . '-metatag-' . $manager;
-
-            $existingCacheEntry = false;
-            if ($cache instanceof FrontendInterface && $properties = $cache->get($cacheIdentifier)) {
-                $existingCacheEntry = true;
-            } else {
-                $properties = $managerObject->renderAllProperties();
-            }
-
+            $properties = $managerObject->renderAllProperties();
             if (!empty($properties)) {
                 $metaTags[] = $properties;
-
-                if ($cache instanceof FrontendInterface && !$existingCacheEntry && ($this->getTypoScriptFrontendController()->page['uid'] ?? false)) {
-                    $cache->set(
-                        $cacheIdentifier,
-                        $properties,
-                        ['pageId_' . $this->getTypoScriptFrontendController()->page['uid']],
-                        $this->getTypoScriptFrontendController()->get_cache_timeout()
-                    );
-                }
             }
         }
         return $metaTags;
index 7e182f5..e20a587 100644 (file)
@@ -16,14 +16,10 @@ namespace TYPO3\CMS\Core\PageTitle;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Cache\CacheManager;
-use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
-use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Service\DependencyOrderingService;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\TypoScript\TypoScriptService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
  * This class will take care of the different providers and returns the title with the highest priority
@@ -31,16 +27,6 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 class PageTitleProviderManager implements SingletonInterface
 {
     /**
-     * @var FrontendInterface
-     */
-    protected $pageCache;
-
-    public function __construct()
-    {
-        $this->initCaches();
-    }
-
-    /**
      * @return string
      * @throws \TYPO3\CMS\Core\Cache\Exception
      * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException
@@ -56,22 +42,10 @@ class PageTitleProviderManager implements SingletonInterface
             ->orderByDependencies($titleProviders);
 
         foreach ($orderedTitleProviders as $provider => $configuration) {
-            $cacheIdentifier =  $this->getTypoScriptFrontendController()->newHash . '-titleTag-' . $provider;
-            if ($this->pageCache instanceof FrontendInterface &&
-                $pageTitle = $this->pageCache->get($cacheIdentifier)
-            ) {
-                break;
-            }
             if (class_exists($configuration['provider']) && is_subclass_of($configuration['provider'], PageTitleProviderInterface::class)) {
                 /** @var PageTitleProviderInterface $titleProviderObject */
                 $titleProviderObject = GeneralUtility::makeInstance($configuration['provider']);
                 if ($pageTitle = $titleProviderObject->getTitle()) {
-                    $this->pageCache->set(
-                        $cacheIdentifier,
-                        $pageTitle,
-                        ['pageId_' . $this->getTypoScriptFrontendController()->page['uid']],
-                        $this->getTypoScriptFrontendController()->get_cache_timeout()
-                    );
                     break;
                 }
             }
@@ -81,14 +55,6 @@ class PageTitleProviderManager implements SingletonInterface
     }
 
     /**
-     * @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
-     */
-    private function getTypoScriptFrontendController(): TypoScriptFrontendController
-    {
-        return $GLOBALS['TSFE'];
-    }
-
-    /**
      * Get the TypoScript configuration for pageTitleProviders
      * @return array
      */
@@ -96,25 +62,13 @@ class PageTitleProviderManager implements SingletonInterface
     {
         $typoscriptService = GeneralUtility::makeInstance(TypoScriptService::class);
         $config = $typoscriptService->convertTypoScriptArrayToPlainArray(
-            $this->getTypoScriptFrontendController()->config['config'] ?? []
+            $GLOBALS['TSFE']->config['config'] ?? []
         );
 
         return $config['pageTitleProviders'] ?? [];
     }
 
     /**
-     * Initializes the caching system.
-     */
-    protected function initCaches(): void
-    {
-        try {
-            $this->pageCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('pages');
-        } catch (NoSuchCacheException $e) {
-            // Intended fall-through
-        }
-    }
-
-    /**
      * @param array $orderInformation
      * @return string[]
      * @throws \UnexpectedValueException
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/Controller/MetaPluginController.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/Controller/MetaPluginController.php
new file mode 100644 (file)
index 0000000..3315763
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\TestMeta\Controller;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\TestMeta\PageTitle\CustomPageTitleProvider;
+
+class MetaPluginController
+{
+    /**
+     * @param string Empty string (no content to process)
+     * @param array TypoScript configuration
+     * @return string
+     */
+    public function setMetaData($content, $configuration): string
+    {
+        $pageId = $GLOBALS['TYPO3_REQUEST']->getQueryParams()['id'];
+        GeneralUtility::makeInstance(CustomPageTitleProvider::class)
+            ->setTitle('static title with pageId: ' . $pageId . ' and pluginNumber: ' . $configuration['pluginNumber']);
+        $metaTagManager = GeneralUtility::makeInstance(MetaTagManagerRegistry::class)->getManagerForProperty('og:title');
+        $metaTagManager->addProperty(
+            'og:title',
+            'OG title from a controller with pageId: ' . $pageId . ' and pluginNumber: ' . $configuration['pluginNumber'],
+            [],
+            true
+        );
+        return 'TYPO3\CMS\TestMeta\Controller::setMetaData';
+    }
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/PageTitle/CustomPageTitleProvider.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/PageTitle/CustomPageTitleProvider.php
new file mode 100644 (file)
index 0000000..e8b0d23
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\TestMeta\PageTitle;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\PageTitle\AbstractPageTitleProvider;
+
+class CustomPageTitleProvider extends AbstractPageTitleProvider
+{
+    public function setTitle(string $title): void
+    {
+        $this->title = $title;
+    }
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page1.typoscript b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page1.typoscript
new file mode 100644 (file)
index 0000000..d15abae
--- /dev/null
@@ -0,0 +1,12 @@
+config.pageTitleProviders {
+  testMetaProvider {
+    provider = TYPO3\CMS\TestMeta\PageTitle\CustomPageTitleProvider
+    before = record
+    after = altPageTitle
+  }
+}
+
+page = PAGE
+page.5 = TEXT
+page.5.value = MetaData-Test
+page.5.stdWrap.wrap = <h1>|</h1>
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page2.typoscript b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page2.typoscript
new file mode 100644 (file)
index 0000000..ea0c01c
--- /dev/null
@@ -0,0 +1,23 @@
+config.pageTitleProviders {
+  testMetaProvider {
+    provider = TYPO3\CMS\TestMeta\PageTitle\CustomPageTitleProvider
+    before = record
+    after = altPageTitle
+  }
+}
+
+page = PAGE
+page.5 = TEXT
+page.5.value = MetaData-Test
+page.5.stdWrap.wrap = <h1>|</h1>
+
+page.10 = USER
+page.10 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 10
+}
+page.20 = USER
+page.20 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 20
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page3.typoscript b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page3.typoscript
new file mode 100644 (file)
index 0000000..486409c
--- /dev/null
@@ -0,0 +1,23 @@
+config.pageTitleProviders {
+  testMetaProvider {
+    provider = TYPO3\CMS\TestMeta\PageTitle\CustomPageTitleProvider
+    before = record
+    after = altPageTitle
+  }
+}
+
+page = PAGE
+page.5 = TEXT
+page.5.value = MetaData-Test
+page.5.stdWrap.wrap = <h1>|</h1>
+
+page.10 = USER_INT
+page.10 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 10
+}
+page.20 = USER_INT
+page.20 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 20
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page4.typoscript b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page4.typoscript
new file mode 100644 (file)
index 0000000..d3424a7
--- /dev/null
@@ -0,0 +1,23 @@
+config.pageTitleProviders {
+  testMetaProvider {
+    provider = TYPO3\CMS\TestMeta\PageTitle\CustomPageTitleProvider
+    before = record
+    after = altPageTitle
+  }
+}
+
+page = PAGE
+page.5 = TEXT
+page.5.value = MetaData-Test
+page.5.stdWrap.wrap = <h1>|</h1>
+
+page.10 = USER
+page.10 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 10
+}
+page.20 = USER_INT
+page.20 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 20
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page5.typoscript b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page5.typoscript
new file mode 100644 (file)
index 0000000..eaa68e4
--- /dev/null
@@ -0,0 +1,23 @@
+config.pageTitleProviders {
+  testMetaProvider {
+    provider = TYPO3\CMS\TestMeta\PageTitle\CustomPageTitleProvider
+    before = record
+    after = altPageTitle
+  }
+}
+
+page = PAGE
+page.5 = TEXT
+page.5.value = MetaData-Test
+page.5.stdWrap.wrap = <h1>|</h1>
+
+page.10 = USER_INT
+page.10 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 10
+}
+page.20 = USER
+page.20 {
+  userFunc = TYPO3\CMS\TestMeta\Controller\MetaPluginController->setMetaData
+  pluginNumber = 20
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/ext_emconf.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/ext_emconf.php
new file mode 100644 (file)
index 0000000..7427484
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+$EM_CONF[$_EXTKEY] = [
+    'title' => 'MetaData Test',
+    'description' => 'MetaData Test',
+    'category' => 'example',
+    'version' => '10.0.0',
+    'state' => 'beta',
+    'clearCacheOnLoad' => 0,
+    'author' => 'Frank N├Ągler',
+    'author_email' => 'frank.naegler@typo3.org',
+    'author_company' => '',
+    'constraints' => [
+        'depends' => [
+            'typo3' => '10.0.0',
+            'seo' => '10.0.0',
+        ],
+        'conflicts' => [],
+        'suggests' => [],
+    ],
+];
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Scenarios/pages_with_plugins_seo_meta.xml b/typo3/sysext/core/Tests/Functional/Fixtures/Scenarios/pages_with_plugins_seo_meta.xml
new file mode 100644 (file)
index 0000000..cbd8730
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dataset>
+    <pages>
+        <uid>1</uid>
+        <pid>0</pid>
+        <title>Rootpage for Tests</title>
+        <deleted>0</deleted>
+    </pages>
+    <pages>
+        <uid>2</uid>
+        <pid>1</pid>
+        <title>Page with USER Plugins</title>
+        <deleted>0</deleted>
+    </pages>
+    <pages>
+        <uid>3</uid>
+        <pid>1</pid>
+        <title>Page with USER_INT Plugins</title>
+        <deleted>0</deleted>
+    </pages>
+    <pages>
+        <uid>4</uid>
+        <pid>1</pid>
+        <title>Page with USER_INT and USER Plugins</title>
+        <deleted>0</deleted>
+    </pages>
+    <pages>
+        <uid>5</uid>
+        <pid>1</pid>
+        <title>Page with USER and USER_INT Plugins</title>
+        <deleted>0</deleted>
+    </pages>
+</dataset>
diff --git a/typo3/sysext/core/Tests/Functional/MetaDataHandling/PluginsTest.php b/typo3/sysext/core/Tests/Functional/MetaDataHandling/PluginsTest.php
new file mode 100644 (file)
index 0000000..7dc8e60
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\Tests\Functional\MetaDataHandling;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Frontend\Tests\Functional\SiteHandling\AbstractTestCase;
+use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
+
+/**
+ * Functional test for the DataHandler
+ */
+class PluginsTest extends AbstractTestCase
+{
+    protected function setUp(): void
+    {
+        $this->testExtensionsToLoad[] = 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta';
+
+        parent::setUp();
+        $this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/core/Tests/Functional/Fixtures/Scenarios/pages_with_plugins_seo_meta.xml');
+
+        $this->writeSiteConfiguration(
+            'website-local',
+            $this->buildSiteConfiguration(1, 'http://localhost/'),
+            [
+                $this->buildDefaultLanguageConfiguration('EN', '/')
+            ]
+        );
+    }
+
+    public function ensurePageSetupIsOkDataProvider(): array
+    {
+        return [
+            'page:uid:1' => [1, false],
+            'page:uid:2' => [2, true],
+            'page:uid:3' => [3, true],
+            'page:uid:4' => [4, true],
+            'page:uid:5' => [5, true],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider ensurePageSetupIsOkDataProvider
+     * @param int $pageId
+     * @param bool $expectPluginOutput
+     */
+    public function ensurePageSetupIsOk(int $pageId, bool $expectPluginOutput): void
+    {
+        $this->setUpFrontendRootPage(1, ['typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page' . $pageId . '.typoscript']);
+        $response = $this->executeFrontendRequest(
+            (new InternalRequest('http://localhost/'))->withQueryParameters([
+                'id' => $pageId,
+            ])
+        );
+        $body = (string)$response->getBody();
+        $this->assertStringContainsString('<h1>MetaData-Test</h1>', $body);
+        if ($expectPluginOutput) {
+            $this->assertStringContainsString('TYPO3\CMS\TestMeta\Controller::setMetaData', $body);
+        } else {
+            $this->assertStringNotContainsString('TYPO3\CMS\TestMeta\Controller::setMetaData', $body);
+        }
+    }
+
+    public function ensureMetaDataAreCorrectDataProvider(): array
+    {
+        return [
+            'page:uid:1' => [1, 'Rootpage for Tests', ''],
+            'page:uid:2' => [2, 'static title with pageId: 2 and pluginNumber: 20', 'OG title from a controller with pageId: 2 and pluginNumber: 20'],
+            'page:uid:3' => [3, 'static title with pageId: 3 and pluginNumber: 20', 'OG title from a controller with pageId: 3 and pluginNumber: 20'],
+            'page:uid:4' => [4, 'static title with pageId: 4 and pluginNumber: 20', 'OG title from a controller with pageId: 4 and pluginNumber: 20'],
+            'page:uid:5' => [5, 'static title with pageId: 5 and pluginNumber: 10', 'OG title from a controller with pageId: 5 and pluginNumber: 10'],
+        ];
+    }
+
+    /**
+     * This test ensures that the meta data and title of the page are the same
+     * even if the pages is delivered cached or uncached.
+     *
+     * @test
+     * @dataProvider ensureMetaDataAreCorrectDataProvider
+     * @param int $pageId
+     * @param string $expectedTitle
+     * @param string $expectedMetaOgTitle
+     */
+    public function ensureMetaDataAreCorrect(int $pageId, string $expectedTitle, string $expectedMetaOgTitle): void
+    {
+        $this->setUpFrontendRootPage(1, ['typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Configuration/TypoScript/page' . $pageId . '.typoscript']);
+
+        // First hit to create a cached version
+        $uncachedResponse = $this->executeFrontendRequest(
+            (new InternalRequest('http://localhost/'))->withQueryParameters([
+                'id' => $pageId,
+            ])
+        );
+        $body = (string)$uncachedResponse->getBody();
+        $this->assertStringContainsString('<title>' . $expectedTitle . '</title>', $body);
+        if ($expectedMetaOgTitle !== '') {
+            $this->assertStringContainsString('<meta name="og:title" content="' . $expectedMetaOgTitle . '" />', $body, 'first hit, not cached');
+        }
+
+        // Second hit to check the cached version
+        $cachedResponse = $this->executeFrontendRequest(
+            (new InternalRequest('http://localhost/'))->withQueryParameters([
+                'id' => $pageId,
+            ])
+        );
+        $body = (string)$cachedResponse->getBody();
+        $this->assertStringContainsString('<title>' . $expectedTitle . '</title>', $body);
+        if ($expectedMetaOgTitle !== '') {
+            $this->assertStringContainsString('<meta name="og:title" content="' . $expectedMetaOgTitle . '" />', $body, 'second hit, cached');
+        }
+    }
+}