cacheTagStack = new \SplStack(); } public function getCacheTagStack(): \SplStack { return $this->cacheTagStack; } /** * Clears the page cache * * @param int|int[]|string $pageIdsToClear single or multiple pageIds to clear the cache for * @todo This method should be hardened to only accept integers or an array of integers */ public function clearPageCache($pageIdsToClear = null): void { if ($pageIdsToClear === null) { $this->cacheManager->flushCachesInGroup('pages'); } else { if (!is_array($pageIdsToClear)) { $pageIdsToClear = [(int)$pageIdsToClear]; } $tags = array_map(static fn(int $item): string => 'pageId_' . $item, $pageIdsToClear); $this->cacheManager->flushCachesInGroupByTags('pages', $tags); } } /** * First, this method checks, if any records are registered (usually via Database Backend) * to be analyzed for a page record, if so, adds additional page IDs to the pageIdStack. * * Walks through the pageIdStack, collects all pageIds * as array and passes them on to clearPageCache. */ public function clearCachesOfRegisteredPageIds(): void { $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); if (!empty($frameworkConfiguration['persistence']['enableAutomaticCacheClearing'] ?? false)) { foreach ($this->clearCacheForTables as $table => $ids) { foreach ($ids as $id) { $this->clearPageCacheForGivenRecord($table, $id); } } } if (!$this->cacheTagStack->isEmpty()) { $cacheTags = []; while (!$this->cacheTagStack->isEmpty()) { $cacheTagValue = $this->cacheTagStack->pop(); // Add fallback to old behavior. Pushing pageIds directly to the stack is possible. So we need to handle int values as well. $cacheTags[] = is_int($cacheTagValue) ? sprintf('pageId_%s', $cacheTagValue) : (string)$cacheTagValue; } $cacheTags = array_values(array_unique($cacheTags)); $this->cacheManager->flushCachesInGroupByTags('pages', $cacheTags); } } /** * Stores a record into the stack to resolve the page IDs later-on to clear the caches on these pages * then. * * Make sure to call clearCachesOfRegisteredPageIds() afterwards. * * @param string $table * @param int $uid */ public function clearCacheForRecord(string $table, int $uid): void { if (!is_array($this->clearCacheForTables[$table] ?? null)) { $this->clearCacheForTables[$table] = []; } $this->clearCacheForTables[$table][] = $uid; } /** * Finds the right PID(s) of a given record and loads the TYPO3 page cache for the given record. * If the record lies on a page, then we clear the cache of this page. * If the record has no PID column, we clear the cache of the current page as best-effort. * * Much of this functionality is taken from DataHandler::clear_cache() which unfortunately only works with logged-in BE user. * * @param string $tableName Table name of the record * @param int $uid UID of the record */ protected function clearPageCacheForGivenRecord(string $tableName, int $uid): void { $pageIdsToClear = []; $storagePage = null; $this->getCacheTagStack()->push($tableName); $this->getCacheTagStack()->push(sprintf('%s_%s', $tableName, $uid)); $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName); $queryBuilder->getRestrictions()->removeAll(); $result = $queryBuilder ->select('pid') ->from($tableName) ->where( $queryBuilder->expr()->eq( 'uid', $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT) ) ) ->executeQuery(); if ($row = $result->fetchAssociative()) { $storagePage = $row['pid']; $pageIdsToClear[] = $storagePage; } if ($storagePage === null) { return; } $pageTS = BackendUtility::getPagesTSconfig($storagePage); if (isset($pageTS['TCEMAIN.']['clearCacheCmd'])) { $clearCacheCommands = GeneralUtility::trimExplode(',', strtolower((string)$pageTS['TCEMAIN.']['clearCacheCmd']), true); $clearCacheCommands = array_unique($clearCacheCommands); foreach ($clearCacheCommands as $clearCacheCommand) { if (MathUtility::canBeInterpretedAsInteger($clearCacheCommand)) { $pageIdsToClear[] = $clearCacheCommand; } } } foreach ($pageIdsToClear as $pageIdToClear) { $this->getCacheTagStack()->push('pageId_' . $pageIdToClear); $this->getCacheTagStack()->push(sprintf('%s_pid_%s', $tableName, $pageIdToClear)); } } }