From c17ea6d0427d1c96b7f9e6c43767c68c852e6be7 Mon Sep 17 00:00:00 2001 From: Lamm Date: Thu, 6 Mar 2025 09:24:47 +0100 Subject: [PATCH] Fix race condition on flushByTag function --- .../Cache/Backend/SsiIncludeCacheBackend.php | 78 +++++-------------- README.md | 4 - Tests/Classes/SsiIncludeCacheBackendTest.php | 10 +-- Tests/Classes/SsiIncludeCacheFrontendTest.php | 2 + composer.json | 4 +- ext_localconf.php | 9 --- runTest.sh | 8 ++ 7 files changed, 33 insertions(+), 82 deletions(-) create mode 100644 runTest.sh diff --git a/Classes/Cache/Backend/SsiIncludeCacheBackend.php b/Classes/Cache/Backend/SsiIncludeCacheBackend.php index 39161ce..3beb826 100644 --- a/Classes/Cache/Backend/SsiIncludeCacheBackend.php +++ b/Classes/Cache/Backend/SsiIncludeCacheBackend.php @@ -6,23 +6,15 @@ use AUS\SsiInclude\Utility\FilenameUtility; use InvalidArgumentException; -use TYPO3\CMS\Core\Cache\Backend\AbstractBackend; -use TYPO3\CMS\Core\Cache\Backend\BackendInterface; -use TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface; -use TYPO3\CMS\Core\Cache\CacheManager; +use TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend; use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException; -use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Utility\GeneralUtility; use Webimpress\SafeWriter\Exception\ExceptionInterface; use Webimpress\SafeWriter\FileWriter; -class SsiIncludeCacheBackend extends AbstractBackend implements TaggableBackendInterface +class SsiIncludeCacheBackend extends Typo3DatabaseBackend { - private readonly TaggableBackendInterface $concrete; - - private string $concreteCache = 'aus_ssi_include_concrete_cache'; - private readonly FilenameUtility $filenameUtility; /** @@ -35,30 +27,26 @@ class SsiIncludeCacheBackend extends AbstractBackend implements TaggableBackendI /** * @inheritdoc * @param array $options - * @throws NoSuchCacheException */ public function __construct($context, array $options = []) { parent::__construct($context, $options); $this->filenameUtility = GeneralUtility::makeInstance(FilenameUtility::class); + } - $cacheManager = GeneralUtility::makeInstance(CacheManager::class); - assert($cacheManager instanceof CacheManager); - $concrete = $cacheManager->getCache($this->concreteCache)->getBackend(); - assert($concrete instanceof BackendInterface); - assert($concrete instanceof TaggableBackendInterface); - $this->concrete = $concrete; + public function getSsiIncludeDir(): string + { + return $this->ssiIncludeDir; } /** - * The cache identifier of the concrete cache which is used to save - * the data. If storeData is false, it also creates an empty cache entry - * to include with caching framework and have the identifier tied to the cache tags and lifetime + * @return list */ - public function setConcreteCache(string $concreteCache): void + private function getSsiIncludeDirFiles(): array { - $this->concreteCache = $concreteCache; + $publicIncludeDir = Environment::getPublicPath() . $this->ssiIncludeDir; + return glob($publicIncludeDir . '*.html') ?: []; } /** @@ -78,19 +66,6 @@ public function setStoreData(bool $storeData): void $this->storeData = $storeData; } - public function getSsiIncludeDir(): string - { - return $this->ssiIncludeDir; - } - - /** - * @inheritdoc - */ - public function setCache(FrontendInterface $cache): void - { - $this->cache = $cache; - } - /** * @inheritdoc * @param array $tags @@ -102,6 +77,8 @@ public function set($entryIdentifier, $data, array $tags = [], $lifetime = null) throw new InvalidArgumentException('Data must be a string', 1616420133); } + parent::set($entryIdentifier, $this->storeData ? $data : '', $tags, $lifetime); + $absolutePath = $this->filenameUtility->getAbsoluteFilename($entryIdentifier); GeneralUtility::mkdir_deep(dirname($absolutePath)); @@ -109,16 +86,6 @@ public function set($entryIdentifier, $data, array $tags = [], $lifetime = null) FileWriter::writeFile($absolutePath, $data); GeneralUtility::fixPermissions($absolutePath); - - $this->concrete->set($entryIdentifier, $this->storeData ? $data : '', $tags, $lifetime); - } - - /** - * @inheritdoc - */ - public function get($entryIdentifier): false|string - { - return $this->concrete->get($entryIdentifier); } /** @@ -127,7 +94,7 @@ public function get($entryIdentifier): false|string */ public function has($entryIdentifier): bool { - $data = $this->concrete->has($entryIdentifier); + $data = parent::has($entryIdentifier); if (!$data) { return false; } @@ -147,7 +114,7 @@ public function remove($entryIdentifier): bool unlink($absoluteFile); } - return $this->concrete->remove($entryIdentifier); + return parent::remove($entryIdentifier); } /** @@ -159,7 +126,7 @@ public function flush(): void unlink($file); } - $this->concrete->flush(); + parent::flush(); } /** @@ -169,7 +136,7 @@ public function flush(): void public function collectGarbage(): void { // remove outdated things - $this->concrete->collectGarbage(); + parent::collectGarbage(); // get all files, and the file that has no entry remove them $files = $this->getSsiIncludeDirFiles(); @@ -200,7 +167,7 @@ public function flushByTag($tag): void */ public function flushByTags(array $tags): void { - $this->concrete->flushByTags($tags); + parent::flushByTags($tags); foreach ($tags as $tag) { $this->flushByTag($tag); } @@ -213,15 +180,6 @@ public function flushByTags(array $tags): void */ public function findIdentifiersByTag($tag): array { - return $this->concrete->findIdentifiersByTag($tag); - } - - /** - * @return list - */ - private function getSsiIncludeDirFiles(): array - { - $publicIncludeDir = Environment::getPublicPath() . $this->ssiIncludeDir; - return glob($publicIncludeDir . '*.html') ?: []; + return parent::findIdentifiersByTag($tag); } } diff --git a/README.md b/README.md index 228c44f..4c865e3 100644 --- a/README.md +++ b/README.md @@ -101,10 +101,6 @@ This defaults to on, but if you want to spare some space you can disable it. Public directory where the SSI files are stored. -#### concreteCache - -An configured taggable cache to use as the cache backends backbone - ### Using the LazyDataProcessor to increase the Performance even more. #### before: diff --git a/Tests/Classes/SsiIncludeCacheBackendTest.php b/Tests/Classes/SsiIncludeCacheBackendTest.php index 803958c..50183aa 100644 --- a/Tests/Classes/SsiIncludeCacheBackendTest.php +++ b/Tests/Classes/SsiIncludeCacheBackendTest.php @@ -64,10 +64,6 @@ private function initializeCacheFramework(): void $cacheManager = GeneralUtility::makeInstance(CacheManager::class); assert($cacheManager instanceof CacheManager); $cacheManager->setCacheConfigurations([ - 'aus_ssi_include_concrete_cache' => [ - 'frontend' => VariableFrontend::class, - 'backend' => Typo3DatabaseBackend::class, - ], 'aus_ssi_include_cache' => [ 'frontend' => SsiIncludeCacheFrontend::class, 'backend' => SsiIncludeCacheBackend::class, @@ -84,11 +80,11 @@ private function initializeCacheFramework(): void */ public function cacheTableExists(): void { - $connection = $this->getConnectionPool()->getConnectionForTable('cache_aus_ssi_include_concrete_cache'); - $query = $connection->executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='cache_aus_ssi_include_concrete_cache'"); + $connection = $this->getConnectionPool()->getConnectionForTable('cache_aus_ssi_include_cache'); + $query = $connection->executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='cache_aus_ssi_include_cache'"); $result = $query->fetchAssociative(); - self::assertNotEmpty($result, 'The cache_ssi_cache table was not created.'); + self::assertNotEmpty($result, 'The cache_aus_ssi_include_cache table was not created.'); } /** diff --git a/Tests/Classes/SsiIncludeCacheFrontendTest.php b/Tests/Classes/SsiIncludeCacheFrontendTest.php index 3e41cb6..eabf9a4 100644 --- a/Tests/Classes/SsiIncludeCacheFrontendTest.php +++ b/Tests/Classes/SsiIncludeCacheFrontendTest.php @@ -9,6 +9,8 @@ class SsiIncludeCacheFrontendTest extends UnitTestCase { + protected bool $resetSingletonInstances = true; + /** * @test */ diff --git a/composer.json b/composer.json index 4e883e0..029f8c7 100644 --- a/composer.json +++ b/composer.json @@ -12,8 +12,8 @@ ], "require": { "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "typo3/cms-fluid": "^10.4.0 || ^11.5.0 || ^12.4.0", - "typo3/cms-frontend": "^10.4.0 || ^11.5.0 || ^12.4.0", + "typo3/cms-fluid": "^11.5.0 || ^12.4.0", + "typo3/cms-frontend": "^11.5.0 || ^12.4.0", "webimpress/safe-writer": "^2.2.0" }, "require-dev": { diff --git a/ext_localconf.php b/ext_localconf.php index 580b663..934bcf7 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -13,15 +13,6 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'][] = ClearCache::class . '->clearCache'; -// we might want to have a default concrete cache if not already defined in another way -if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['aus_ssi_include_cache']['options']['concreteCache'])) { - $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['aus_ssi_include_concrete_cache'] = [ - 'frontend' => VariableFrontend::class, - 'backend' => Typo3DatabaseBackend::class, - ]; -} - -// TODO nochmal testen wie die ergebnisse sind jenachdem was ich im projekt hinterlege // define the main cache with the possibility to have a partial configuration already $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['aus_ssi_include_cache'] = array_merge([ 'frontend' => SsiIncludeCacheFrontend::class, diff --git a/runTest.sh b/runTest.sh new file mode 100644 index 0000000..e128865 --- /dev/null +++ b/runTest.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.1-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^11" -q -n -W --dev && composer test' +docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.1-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^12" -q -n -W --dev && composer test' +docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.2-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^11" -q -n -W --dev && composer test' +docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.2-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^12" -q -n -W --dev && composer test' +docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.3-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^11" -q -n -W --dev && composer test' +docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.3-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^12" -q -n -W --dev && composer test'