From e79e881b04ec27a202588f501fe5892cfc671261 Mon Sep 17 00:00:00 2001 From: Daniel Huf Date: Fri, 8 Nov 2024 15:42:50 +0100 Subject: [PATCH] fix: Caching issue --- Classes/EventListener/ModifyCacheLifetime.php | 29 +++++++++++ Classes/Hooks/ContentProcessor.php | 14 ------ Classes/Hooks/DataHandler.php | 50 ++++++++++++------- Classes/Service/VariablesService.php | 44 +++++++++------- Configuration/Services.yaml | 7 ++- 5 files changed, 92 insertions(+), 52 deletions(-) create mode 100644 Classes/EventListener/ModifyCacheLifetime.php diff --git a/Classes/EventListener/ModifyCacheLifetime.php b/Classes/EventListener/ModifyCacheLifetime.php new file mode 100644 index 0000000..1ec604d --- /dev/null +++ b/Classes/EventListener/ModifyCacheLifetime.php @@ -0,0 +1,29 @@ +setCacheLifetime( + min( + $event->getCacheLifetime(), + $this->variablesService->getLifetime(), + ) + ); + } +} diff --git a/Classes/Hooks/ContentProcessor.php b/Classes/Hooks/ContentProcessor.php index 89e9813..cf62bba 100644 --- a/Classes/Hooks/ContentProcessor.php +++ b/Classes/Hooks/ContentProcessor.php @@ -17,7 +17,6 @@ use Sinso\Variables\Service\VariablesService; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent; class ContentProcessor @@ -29,23 +28,10 @@ public function __construct() $this->variablesService = GeneralUtility::makeInstance(VariablesService::class); } - /** - * for v12 from the Service.yaml - */ public function __invoke(AfterCacheableContentIsGeneratedEvent $event): void { $extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class); $this->variablesService->initialize($extensionConfiguration, $event->getController()); $this->variablesService->replaceMarkersInStructureAndAdjustCaching($event->getController()->content); } - - /** - * for the v11 - */ - public function replaceContent(array &$parameters, TypoScriptFrontendController $parentObject): void - { - $extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class); - $this->variablesService->initialize($extensionConfiguration, $parentObject); - $this->variablesService->replaceMarkersInStructureAndAdjustCaching($parentObject->content); - } } diff --git a/Classes/Hooks/DataHandler.php b/Classes/Hooks/DataHandler.php index 4f44607..2e00cb0 100644 --- a/Classes/Hooks/DataHandler.php +++ b/Classes/Hooks/DataHandler.php @@ -14,51 +14,64 @@ namespace Sinso\Variables\Hooks; +use Sinso\Variables\Domain\Model\Marker; use Sinso\Variables\Utility\CacheKeyUtility; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; -use TYPO3\CMS\Core\Utility\GeneralUtility; class DataHandler { public function __construct( private readonly ConnectionPool $connectionPool, + private readonly CacheManager $cacheManager, ) {} /** * Flushes the cache if a marker record was edited. */ - public function clearCachePostProc(array $params, \TYPO3\CMS\Core\DataHandling\DataHandler $that): void + public function clearCachePostProc(array $params, \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler): void + { + $marker = $this->getMarkerFromHook($params, $dataHandler); + + if (!$marker) { + return; + } + + $cacheTagToFlush = CacheKeyUtility::getCacheKey( + $marker->getMarkerWithBrackets() + ); + + $this->cacheManager->flushCachesInGroupByTag('pages', $cacheTagToFlush); + } + + protected function getMarkerFromHook(array $params, \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler): ?Marker { if ( ($params['table'] !== 'tx_variables_marker') || !isset($params['uid']) ) { - return; + return null; } - // TODO: Prüfen, was passiert, wenn der Marker-Key nicht angepasst wird, oder ein Element gelöscht oder hidden wird - $marker = $that->datamap[$params['table']][$params['uid']]['marker'] ?? null; + $marker = $dataHandler->datamap[$params['table']][$params['uid']]['marker'] ?? null; - if ($marker === null || $marker === '') { - $marker = $this->findDeletedVariableMarkerByUid($params['uid']); - if ($marker === null || $marker === '') { - return; - } + if (!$marker) { + $marker = $this->findVariableMarkerByUidEventIfHiddenOrDeleted($params['uid']); } - $cacheTagsToFlush = []; - $cacheTagsToFlush[] = CacheKeyUtility::getCacheKey($marker); - - $cacheManager = GeneralUtility::makeInstance(CacheManager::class); - foreach ($cacheTagsToFlush as $cacheTag) { - $cacheManager->flushCachesInGroupByTag('pages', $cacheTag); + if (!$marker) { + return null; } + + return new Marker( + uid: $params['uid'], + key: $marker, + replacement: '', // value doesn't matter here + ); } - protected function findDeletedVariableMarkerByUid(int $uid): ?string + protected function findVariableMarkerByUidEventIfHiddenOrDeleted(int $uid): ?string { $queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_variables_marker'); $queryBuilder->getRestrictions()->removeAll(); @@ -67,7 +80,6 @@ protected function findDeletedVariableMarkerByUid(int $uid): ?string ->from('tx_variables_marker') ->where( $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)), - $queryBuilder->expr()->eq('deleted', 1), ) ->executeQuery() ->fetchOne(); diff --git a/Classes/Service/VariablesService.php b/Classes/Service/VariablesService.php index 700aaf7..63a1c80 100644 --- a/Classes/Service/VariablesService.php +++ b/Classes/Service/VariablesService.php @@ -66,7 +66,7 @@ public function replaceMarkersInStructureAndAdjustCaching( throw new \Exception('Markers not initialized. Please run initialize() first.', 1726241619); } $this->replaceMarkersInStructure($structure); - $this->setCacheTagsAndLifetimeInTsfe(); + $this->setCacheTagsInTsfe(); } /** @@ -111,12 +111,18 @@ protected function replaceMarkersInText(string &$text): void } // Assign a cache key associated with the marker - $this->cacheTags->add(CacheKeyUtility::getCacheKey($marker->getMarkerWithBrackets())); + $this->cacheTags->add( + CacheKeyUtility::getCacheKey( + $marker->getMarkerWithBrackets() + ) + ); $this->usedMarkerKeys[] = $marker->key; $text = $newContent; } } + $this->usedMarkerKeys = array_unique($this->usedMarkerKeys); + // Remove all markers (avoids empty entries) if ($this->extensionConfiguration->get('variables', 'removeUnreplacedMarkers')) { $text = preg_replace('/{{.*?}}/', '', $text); @@ -169,24 +175,30 @@ protected function getMarkers(): MarkerCollection return $markers; } - protected function setCacheTagsAndLifetimeInTsfe(): void + protected function setCacheTagsInTsfe(): void { - $this->usedMarkerKeys = array_unique($this->usedMarkerKeys); - - $minLifetime = min( - $this->getSmallestLifetimeForMarkers($this->usedMarkerKeys), - $this->typoScriptFrontendController->page['cache_timeout'] ?: PHP_INT_MAX - ); - - $this->typoScriptFrontendController->page['cache_timeout'] = $minLifetime; - if (count($this->cacheTags) > 0) { $this->typoScriptFrontendController->addCacheTags($this->cacheTags->toArray()); } } - public function getSmallestLifetimeForMarkers(array $usedMarkerKeys): int + public function getLifetime(): int { + return $this->getNearestTimestampForMarkers($this->usedMarkerKeys) - $GLOBALS['EXEC_TIME']; + } + + /** + * Get the nearest timestamp in the future when changes for Markers should happen. + * This respects starttime and endtime. + * The result will be used to calculate the maximal caching duration + * + * @throws \Doctrine\DBAL\Exception + */ + public function getNearestTimestampForMarkers(array $usedMarkerKeys): int + { + // Max value possible to keep an int \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->realPageCacheContent ($timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;) + $result = PHP_INT_MAX; + $tableName = 'tx_variables_marker'; $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName)->createQueryBuilder(); $queryBuilder->getRestrictions()->removeAll() @@ -195,10 +207,8 @@ public function getSmallestLifetimeForMarkers(array $usedMarkerKeys): int // Code heavily inspired by: // \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->getFirstTimeValueForRecord $now = (int)$GLOBALS['ACCESS_TIME']; - // Max value possible to keep an int \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->realPageCacheContent ($timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;) - $result = PHP_INT_MAX - $GLOBALS['EXEC_TIME']; $timeFields = []; - $timeConditions = $queryBuilder->expr()->orX(); + $timeConditions = $queryBuilder->expr()->or(); foreach (['starttime', 'endtime'] as $field) { if (isset($GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field])) { $timeFields[$field] = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field]; @@ -212,7 +222,7 @@ public function getSmallestLifetimeForMarkers(array $usedMarkerKeys): int . ' THEN NULL ELSE ' . $queryBuilder->quoteIdentifier($timeFields[$field]) . ' END' . ') AS ' . $queryBuilder->quoteIdentifier($timeFields[$field]) ); - $timeConditions->add( + $timeConditions->with( $queryBuilder->expr()->gt( $timeFields[$field], $queryBuilder->createNamedParameter($now, \PDO::PARAM_INT) diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index dc73b13..f2115f4 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -10,10 +10,13 @@ services: Sinso\Variables\Hooks\DataHandler: public: true - Sinso\Variables\Hooks\ContentProcessor : + Sinso\Variables\Hooks\ContentProcessor: tags: - name: event.listener - identifier: 'variables/content-processor' Sinso\Variables\Service\VariablesService: public: true + + Sinso\Variables\EventListener\ModifyCacheLifetime: + tags: + - name: event.listener