From 96706a6aa2c5025e39b9ffa21934c6a3c14d9b09 Mon Sep 17 00:00:00 2001 From: David Steeb Date: Tue, 26 May 2026 21:05:16 +0200 Subject: [PATCH 1/4] [!!!][TASK] Drop TYPO3 v11 / v12 support, add v14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the supported TYPO3 range to ^13.4 || ^14.0 and PHP minimum to ^8.2. Two TYPO3 v14 breaking changes required code adjustments; the rest is just removing the v11-only fallback code that is no longer reachable. * AfterCachedPageIsPersistedEvent no longer carries the TypoScriptFrontendController in v14. The listener now reads cache tags from $event->getCacheData()['cacheTags'], which TYPO3 populates in both v13 and v14 before dispatching the event. * AbstractMessage::OK / WARNING / ERROR constants were removed in v14. ManagementController switches to ContextualFeedbackSeverity::* enum cases unconditionally; the Typo3Version major-version checks are gone. Cleanup of v11-only code: * Classes/Hook/FrontendHook.php and the matching SC_OPTIONS hook in ext_localconf.php — the v11 tslib_fe insertPageIncache hook has been superseded by the AfterCachedPageIsPersistedEvent listener since v12. * ext_tables.php — v11-only ExtensionUtility::registerModule() call. Configuration/Backend/Modules.php has handled the registration since v12. * Resources/Private/TemplatesV11/ — v11 fallback Fluid template. See UPGRADE.md for the v4 -> v5 upgrade notes. --- Classes/Controller/ManagementController.php | 45 ++------------ Classes/Hook/FrontendHook.php | 62 ------------------- Classes/Listener/AfterCacheIsPersisted.php | 2 +- .../TemplatesV11/Management/Index.html | 23 ------- UPGRADE.md | 35 +++++++++++ composer.json | 6 +- ext_emconf.php | 2 +- ext_localconf.php | 5 -- ext_tables.php | 27 -------- 9 files changed, 45 insertions(+), 162 deletions(-) delete mode 100755 Classes/Hook/FrontendHook.php delete mode 100644 Resources/Private/TemplatesV11/Management/Index.html delete mode 100644 ext_tables.php diff --git a/Classes/Controller/ManagementController.php b/Classes/Controller/ManagementController.php index 863aa93..0fdbac4 100644 --- a/Classes/Controller/ManagementController.php +++ b/Classes/Controller/ManagementController.php @@ -23,8 +23,6 @@ use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Http\RedirectResponse; -use TYPO3\CMS\Core\Information\Typo3Version; -use TYPO3\CMS\Core\Messaging\AbstractMessage; use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; @@ -41,78 +39,45 @@ public function __construct(protected ModuleTemplateFactory $moduleTemplateFacto public function indexAction(): ResponseInterface { $moduleTemplate = $this->moduleTemplateFactory->create($this->request); - if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() === 11) { - $this->view->setTemplateRootPaths(['EXT:proxycachemanager/Resources/Private/TemplatesV11/']); - $moduleTemplate->setContent($this->view->render()); - $response = $this->htmlResponse($moduleTemplate->renderContent()); - } else { - $response = $moduleTemplate->renderResponse('Management/Index'); - } - return $response; + return $moduleTemplate->renderResponse('Management/Index'); } - /** - * @param string $tag - */ public function clearTagAction(string $tag): ResponseInterface { GeneralUtility::makeInstance(CacheManager::class)->flushCachesByTags([htmlspecialchars($tag)]); - if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() === 11) { - $severity = AbstractMessage::OK; - } else { - $severity = ContextualFeedbackSeverity::OK; - } $this->addFlashMessage( 'Successfully purged cache tag "' . htmlspecialchars($tag) . '".', 'Cache flushed', - $severity + ContextualFeedbackSeverity::OK ); return new RedirectResponse($this->uriBuilder->reset()->uriFor('index')); } - /** - * @param string $url - */ public function purgeUrlAction(string $url): ResponseInterface { if (empty($url)) { - if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() === 11) { - $severity = AbstractMessage::WARNING; - } else { - $severity = ContextualFeedbackSeverity::WARNING; - } $this->addFlashMessage( 'Please specify url', 'Cache not flushed', - $severity + ContextualFeedbackSeverity::WARNING ); return new RedirectResponse($this->uriBuilder->reset()->uriFor('index')); } $url = htmlspecialchars($url); if (!$this->proxyProvider->isActive()) { - if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() === 11) { - $severity = AbstractMessage::ERROR; - } else { - $severity = ContextualFeedbackSeverity::ERROR; - } $this->addFlashMessage( 'Attempting to purge URL "' . $url . '". No active provider configured.', 'Cache not flushed', - $severity + ContextualFeedbackSeverity::ERROR ); return new RedirectResponse($this->uriBuilder->reset()->uriFor('index')); } $this->proxyProvider->flushCacheForUrls([$url]); - if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() === 11) { - $severity = AbstractMessage::OK; - } else { - $severity = ContextualFeedbackSeverity::OK; - } $this->addFlashMessage( 'Successfully purged URL "' . $url . '".', 'Cache flushed', - $severity + ContextualFeedbackSeverity::OK ); return new RedirectResponse($this->uriBuilder->reset()->uriFor('index')); } diff --git a/Classes/Hook/FrontendHook.php b/Classes/Hook/FrontendHook.php deleted file mode 100755 index ec26ffc..0000000 --- a/Classes/Hook/FrontendHook.php +++ /dev/null @@ -1,62 +0,0 @@ -proxyProvider = $configuration->getProxyProvider(); - } - - /** - * Hook that is called when a cacheable page was just added to the cache system - * calls the proxy cache and stores the pageId, the URL - * this call costs a little bit of performance but is only called - * once (!) as the URL is cacheable, next time it is fetched - * from the reverse proxy directly. - * - * @param TypoScriptFrontendController $frontendController - * @param $timeOutTime - */ - public function insertPageIncache(TypoScriptFrontendController $frontendController, $timeOutTime) - { - if (!$this->proxyProvider->isActive() || !$this->proxyProvider->shouldRequestBeMarkedAsCached($this->getRequest())) { - return; - } - // cache the page URL that was called - $url = (string)$this->getRequest()->getUri(); - $this->cache->set(md5($url), $url, $frontendController->getPageCacheTags(), $timeOutTime); - } - - protected function getRequest(): ?ServerRequestInterface - { - return $GLOBALS['TYPO3_REQUEST']; - } -} diff --git a/Classes/Listener/AfterCacheIsPersisted.php b/Classes/Listener/AfterCacheIsPersisted.php index 2781c48..ea7af46 100644 --- a/Classes/Listener/AfterCacheIsPersisted.php +++ b/Classes/Listener/AfterCacheIsPersisted.php @@ -36,7 +36,7 @@ public function __invoke(AfterCachedPageIsPersistedEvent $event): void if (!$this->proxyProvider->isActive()) { return; } - $cacheTags = $event->getController()->getPageCacheTags(); + $cacheTags = $event->getCacheData()['cacheTags'] ?? []; $url = (string)$event->getRequest()->getUri(); $this->cache->set(md5($url), $url, $cacheTags, $event->getCacheLifetime()); } diff --git a/Resources/Private/TemplatesV11/Management/Index.html b/Resources/Private/TemplatesV11/Management/Index.html deleted file mode 100644 index c9a78b8..0000000 --- a/Resources/Private/TemplatesV11/Management/Index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -

CDN Cache Management

- -
- -

Purge by URL

-
-
- -
- - -
- -
-
-
-
- - diff --git a/UPGRADE.md b/UPGRADE.md index 50202b8..9637366 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,40 @@ # Upgrade Instructions +v4 => v5 + +Proxycachemanager v5 drops support for TYPO3 v11 and v12. The minimum +supported version is now TYPO3 v13 LTS; v14 is fully supported. + +## Removed + +* `Classes/Hook/FrontendHook.php` — the v11-only `tslib/class.tslib_fe.php` + `insertPageIncache` hook. Cache writes are handled exclusively via the + `AfterCachedPageIsPersistedEvent` listener now. +* `Resources/Private/TemplatesV11/` — fallback Fluid template for the v11 + backend module. +* `ext_tables.php` — v11-only `ExtensionUtility::registerModule()` call. + The module is registered through `Configuration/Backend/Modules.php` on + v12 and up. + +## Changed + +* `Classes/Listener/AfterCacheIsPersisted` no longer calls + `$event->getController()->getPageCacheTags()` — TYPO3 v14 dropped the + `controller` argument from `AfterCachedPageIsPersistedEvent`. Cache tags + are now read from `$event->getCacheData()['cacheTags']`, which TYPO3 + populates in both v13 and v14. +* `Classes/Controller/ManagementController` no longer branches on the + TYPO3 major version. `\TYPO3\CMS\Core\Messaging\AbstractMessage::OK`, + `::WARNING`, `::ERROR` were removed in TYPO3 v14; the controller now + always uses `ContextualFeedbackSeverity::*` enum cases. + +## Required platform + +* PHP 8.2+ +* TYPO3 13.4+ or 14.x + +--- + v3 => v4 Proxycachemanager v3 served for many years as a well thought out basis diff --git a/composer.json b/composer.json index 698c1d7..9689712 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,9 @@ } }, "require": { - "php": "^8.1", - "typo3/cms-backend": "^11.5 || ^12.4 || ^13.4", - "typo3/cms-frontend": "^11.5 || ^12.4 || ^13.4" + "php": "^8.2", + "typo3/cms-backend": "^13.4 || ^14.0", + "typo3/cms-frontend": "^13.4 || ^14.0" }, "require-dev": { "saschaegerer/phpstan-typo3": "^1.10", diff --git a/ext_emconf.php b/ext_emconf.php index 6dc7f49..a49e01b 100755 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -12,7 +12,7 @@ 'author_company' => 'b13 GmbH', 'constraints' => [ 'depends' => [ - 'typo3' => '11.5.0-13.4.99', + 'typo3' => '13.4.0-14.99.99', ], 'conflicts' => [ ], diff --git a/ext_localconf.php b/ext_localconf.php index 6966cb9..172e14f 100755 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -17,8 +17,3 @@ 'groups' => ['pages', 'proxy'], ]; } - -// Hook for adding any cacheable frontend URL to our proxy cache -// v11 only -$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache']['tx_proxycachemanager'] = - \B13\Proxycachemanager\Hook\FrontendHook::class; diff --git a/ext_tables.php b/ext_tables.php deleted file mode 100644 index 08e6014..0000000 --- a/ext_tables.php +++ /dev/null @@ -1,27 +0,0 @@ -getMajorVersion() < 12 && - (GeneralUtility::makeInstance(ProxyConfiguration::class))->showBackendModule() -) { - \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule( - 'proxycachemanager', - 'site', - 'cdn_cache', - 'bottom', - [\B13\Proxycachemanager\Controller\ManagementController::class => 'index,clearTag,purgeUrl'], - [ - 'access' => 'user,group', - 'icon' => 'EXT:proxycachemanager/Resources/Public/Icons/CacheModule.png', - 'labels' => 'LLL:EXT:proxycachemanager/Resources/Private/Language/locallang_module_cache.xlf', - 'navigationComponentId' => '', - 'inheritNavigationComponentFromMainModule' => false, - ] - ); -} From 9ef956535db43f52c3820610cd74c66ee827076c Mon Sep 17 00:00:00 2001 From: David Steeb Date: Wed, 27 May 2026 09:26:13 +0200 Subject: [PATCH 2/4] [BUGFIX] Match v14 cache backend parent signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPO3 v14 added strict type declarations to Typo3DatabaseBackend's public API; v13's parent signatures had no types. Loading the extension on v14 fataled with: Declaration of B13\Proxycachemanager\Cache\Backend\ReverseProxyCacheBackend::remove($entryIdentifier) must be compatible with TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::remove(string $entryIdentifier): bool The fix: * Drop the remove() override entirely. It was a pure passthrough to parent::remove() — the docblock explained the historical reason the provider call was removed, but the override itself adds no behaviour. Parent::remove() is inherited automatically. * Add covariant ": void" return types to flush(), flushByTag(), flushByTags() to match v14 while staying compatible with v13's untyped parents (return-type covariance is allowed). Parameter types stay untyped so v13 (untyped parent) keeps working; PHP allows the child to widen — but not narrow — parameter types. --- .../Backend/ReverseProxyCacheBackend.php | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/Classes/Cache/Backend/ReverseProxyCacheBackend.php b/Classes/Cache/Backend/ReverseProxyCacheBackend.php index 2683af0..bb6bdd1 100755 --- a/Classes/Cache/Backend/ReverseProxyCacheBackend.php +++ b/Classes/Cache/Backend/ReverseProxyCacheBackend.php @@ -39,30 +39,11 @@ public function setReverseProxyProvider(ProxyProviderInterface $reverseProxyProv $this->reverseProxyProvider = $reverseProxyProvider; } - /** - * Removes all cache entries matching the specified identifier. - * Usually this only affects one entry. - * - * Please note: As remove() is called when using set() in the parent method, - * it would also flush the cache when the FE is accessed, resulting in a lot of - * cache entries. This should be avoided, for this reason, we do not call the - * provider anymore. Ideally we should not invalidate but rather push this actively - * to the proxy in the future. - * - * @param string $entryIdentifier Specifies the cache entry to remove - * - * @return bool TRUE if (at least) an entry could be removed or FALSE if no entry was found - */ - public function remove($entryIdentifier) - { - return parent::remove($entryIdentifier); - } - /** * Removes all cache entries of this cache. * Also let the proxy provider know to clear everything as well. */ - public function flush() + public function flush(): void { // make the HTTP Purge call if ($this->reverseProxyProvider->isActive()) { @@ -77,7 +58,7 @@ public function flush() * * @param string $tag The tag the entries must have */ - public function flushByTag($tag) + public function flushByTag($tag): void { if ($this->reverseProxyProvider->isActive()) { $identifiers = $this->findIdentifiersByTag($tag); @@ -97,7 +78,7 @@ public function flushByTag($tag) * * @param string[] $tags */ - public function flushByTags(array $tags) + public function flushByTags(array $tags): void { if ($this->reverseProxyProvider->isActive()) { $identifiers = []; From 7509871ef7d27f0ab6ac9cfce7be7d3f8366e7a2 Mon Sep 17 00:00:00 2001 From: David Steeb Date: Wed, 27 May 2026 09:28:00 +0200 Subject: [PATCH 3/4] [BUGFIX] Drop TransientBackendInterface marker on cache backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPO3 v14 changed TransientBackendInterface from an empty marker interface to a proper contract with set(string $entryIdentifier, mixed \$data, ...): void That contract is incompatible with the parent Typo3DatabaseBackend's typed signature set(string \$entryIdentifier, string \$data, ...): void so v14 refuses to load a class that both extends Typo3DatabaseBackend and implements TransientBackendInterface, fataling with: Declaration of TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::set(...) must be compatible with TYPO3\CMS\Core\Cache\Backend\TransientBackendInterface::set(...) ReverseProxyCacheBackend originally declared the marker so the configured VariableFrontend would skip serialize()/unserialize() on the stored URL strings. The optimisation was always minor (we store short strings), and the marker simply cannot coexist with Typo3DatabaseBackend on v14. Dropping the interface declaration returns to the default VariableFrontend behaviour — URLs are serialised/unserialised transparently — which is functionally identical and works on both v13 and v14. --- Classes/Cache/Backend/ReverseProxyCacheBackend.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Classes/Cache/Backend/ReverseProxyCacheBackend.php b/Classes/Cache/Backend/ReverseProxyCacheBackend.php index bb6bdd1..6fa920a 100755 --- a/Classes/Cache/Backend/ReverseProxyCacheBackend.php +++ b/Classes/Cache/Backend/ReverseProxyCacheBackend.php @@ -19,7 +19,6 @@ use B13\Proxycachemanager\Provider\ProxyProviderInterface; use Psr\Log\LoggerAwareTrait; -use TYPO3\CMS\Core\Cache\Backend\TransientBackendInterface; use TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -29,7 +28,7 @@ * when removing or flushing, additionally does a HTTP Request * of course "setting" works naturally in am already working reverse proxy environment. */ -class ReverseProxyCacheBackend extends Typo3DatabaseBackend implements TransientBackendInterface +class ReverseProxyCacheBackend extends Typo3DatabaseBackend { use LoggerAwareTrait; protected ProxyProviderInterface $reverseProxyProvider; From 38638a04b8c43599c4de8800f5a68f5afee7088a Mon Sep 17 00:00:00 2001 From: David Steeb Date: Wed, 27 May 2026 11:31:13 +0200 Subject: [PATCH 4/4] [BUGFIX] Drop FrontendHook service definition The previous commit removed Classes/Hook/FrontendHook.php but missed the matching service definition in Configuration/Services.yaml. With the class gone, every DI container build fataled: Invalid service "B13\Proxycachemanager\Hook\FrontendHook": class "B13\Proxycachemanager\Hook\FrontendHook" does not exist. --- Configuration/Services.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 49b6915..f8b3eb2 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -26,10 +26,6 @@ services: identifier: 'b13-proxycachemanager-afterCacheIsPersisted' arguments: $cache: '@cache.tx_proxy' - B13\Proxycachemanager\Hook\FrontendHook: - public: true - arguments: - $cache: '@cache.tx_proxy' B13\Proxycachemanager\ResourceStorageOperations\: resource: '../Classes/ResourceStorageOperations/*'