Skip to content

[TASK] TYPO3 v14 support (drop v11/v12)#52

Open
davidsteeb wants to merge 5 commits into
masterfrom
feature/typo3-v14-support
Open

[TASK] TYPO3 v14 support (drop v11/v12)#52
davidsteeb wants to merge 5 commits into
masterfrom
feature/typo3-v14-support

Conversation

@davidsteeb

Copy link
Copy Markdown
Contributor

Summary

Adds TYPO3 v14 compatibility and drops the legacy v11/v12 code paths. The
minimum supported version is now TYPO3 v13.4 LTS; v14 is fully supported.

Commits:

  • [!!!][TASK] Drop TYPO3 v11 / v12 support, add v14 (removes the v11
    FrontendHook, the v11 Fluid template fallback and the ext_tables.php
    module registration; module is registered via Configuration/Backend/Modules.php)
  • [BUGFIX] Match v14 cache backend parent signatures
  • [BUGFIX] Drop TransientBackendInterface marker on cache backend — on
    v14 the interface became a typed contract (set(string, mixed, …))
    incompatible with Typo3DatabaseBackend::set(string, string, …), which
    fatals on class load
  • [BUGFIX] Drop FrontendHook service definition
  • [BUGFIX] Deserialize cached URLs before proxy purge — see below

Fixes #51 (regression from dropping the transient marker)

Dropping TransientBackendInterface means the configured VariableFrontend
now serializes (and HMAC-signs on v13.4+) every stored page URL. The
backend's read paths (getAllCachedUrls() raw SELECT, and
flushByTag()/flushByTags() via $this->get()Typo3DatabaseBackend::get())
read that content raw, so the proxy provider received serialized blobs
instead of URLs — the local DB flush still ran, but HTTP PURGE calls hit
invalid URLs and reverse-proxy/CDN invalidation silently stopped working.

Reads now go through the frontend ($this->cache) so entries are
deserialized again, with a fallback to the raw value for rows written by v4
(transient era, plain URLs). Works identically on v13 and v14.

Notes for the reviewer

  • No functional/unit tests exist in the repo (only the phpunit bootstrap),
    so this is unverified against a running TYPO3 instance — a manual smoke
    test of proxy purging (full flush + tag flush) on a real v13 and v14
    setup before merge would be good.
  • UPGRADE.md documents all removals/changes incl. the serialization note.

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.
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.
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.
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.
Dropping TransientBackendInterface in 7509871 left the backend's read
paths reading raw column content. The configured VariableFrontend
serializes (and HMAC-signs on v13.4+) every entry it writes, so since
that commit cache_tx_proxy.content holds serialized blobs, not URLs.

getAllCachedUrls() (raw SELECT) and flushByTag()/flushByTags()
($this->get() resolves to Typo3DatabaseBackend::get(), no unserialize)
therefore handed serialized blobs to ProxyProviderInterface. The local
DB flush still ran, but the HTTP PURGE calls targeted invalid URLs, so
reverse-proxy/CDN invalidation silently stopped working.

Route reads through the frontend ($this->cache) so entries are
deserialized again, and keep a fallback to the raw value for rows
written by v4 (TransientBackendInterface era), which still hold the
plain URL. Works identically on TYPO3 v13 and v14.

Resolves: #51
@davidsteeb davidsteeb requested a review from bmack June 19, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cache_tx_proxy.content is now serialized, but backend reads it raw

1 participant