Skip to content

Commit

Permalink
[BUGFIX] Remove caches for page title and meta tag
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
helhum authored and Richard Haeser committed Jun 21, 2019
1 parent f04022b commit d05865f
Show file tree
Hide file tree
Showing 15 changed files with 379 additions and 90 deletions.
4 changes: 1 addition & 3 deletions typo3/sysext/core/Classes/MetaTag/AbstractMetaTagManager.php
Expand Up @@ -16,9 +16,7 @@
* 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
Expand Down
4 changes: 1 addition & 3 deletions typo3/sysext/core/Classes/MetaTag/GenericMetaTagManager.php
Expand Up @@ -16,13 +16,11 @@
* 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
Expand Down
20 changes: 15 additions & 5 deletions typo3/sysext/core/Classes/MetaTag/MetaTagManagerRegistry.php
Expand Up @@ -26,6 +26,8 @@
class MetaTagManagerRegistry implements SingletonInterface
{
protected $registry = [];
private $instances = [];
private $managers;

public function __construct()
{
Expand Down Expand Up @@ -53,6 +55,7 @@ public function registerManager(string $name, string $className, array $before =
'before' => $before,
'after' => $after
];
$this->managers = null;
}

/**
Expand Down Expand Up @@ -81,25 +84,32 @@ public function getManagerForProperty(string $property): MetaTagManagerInterface
*/
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;
}

/**
* Remove all registered MetaTagManagers
*/
public function removeAllManagers()
{
unset($this->registry);
$this->registry = [];
$this->managers = null;
}
}
42 changes: 10 additions & 32 deletions typo3/sysext/core/Classes/Page/PageRenderer.php
Expand Up @@ -18,7 +18,6 @@
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;
Expand Down Expand Up @@ -170,13 +169,6 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
*/
protected $metaTags = [];

/**
* META Tags added via the API
*
* @var array
*/
protected $metaTagsByAPI = [];

/**
* @var array
*/
Expand Down Expand Up @@ -358,6 +350,15 @@ public function __construct($templateFile = '')
$this->setMetaTag('name', 'generator', 'TYPO3 CMS');
}

/**
* 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
*/
Expand Down Expand Up @@ -918,7 +919,6 @@ public function setMetaTag(string $type, string $name, string $content, array $s
1496402460
);
}

$manager = $this->metaTagRegistry->getManagerForProperty($name);
$manager->addProperty($name, $content, $subProperties, $replace, $type);
}
Expand Down Expand Up @@ -1674,33 +1674,11 @@ protected function renderMetaTagsFromAPI()
{
$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;
Expand Down
48 changes: 1 addition & 47 deletions typo3/sysext/core/Classes/PageTitle/PageTitleProviderManager.php
Expand Up @@ -16,30 +16,16 @@
* 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
*/
class PageTitleProviderManager implements SingletonInterface
{
/**
* @var FrontendInterface
*/
protected $pageCache;

public function __construct()
{
$this->initCaches();
}

/**
* @return string
* @throws \TYPO3\CMS\Core\Cache\Exception
Expand All @@ -56,22 +42,10 @@ public function getTitle(): string
->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;
}
}
Expand All @@ -80,14 +54,6 @@ public function getTitle(): string
return $pageTitle;
}

/**
* @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
*/
private function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}

/**
* Get the TypoScript configuration for pageTitleProviders
* @return array
Expand All @@ -96,24 +62,12 @@ private function getPageTitleProviderConfiguration(): array
{
$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[]
Expand Down
@@ -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';
}
}
@@ -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;
}
}
@@ -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>
@@ -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
}
@@ -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
}

0 comments on commit d05865f

Please sign in to comment.