Skip to content

Commit

Permalink
[FEATURE] Add support for TYPO3 13.4 LTS
Browse files Browse the repository at this point in the history
  • Loading branch information
eliashaeussler committed Nov 14, 2024
1 parent 51912cf commit a293770
Show file tree
Hide file tree
Showing 15 changed files with 237 additions and 34 deletions.
35 changes: 18 additions & 17 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
* text=auto
/.github export-ignore
/Tests export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.php-cs-fixer.php export-ignore
/CODE_OF_CONDUCT.md export-ignore
/composer.lock export-ignore
/CONTRIBUTING.md export-ignore
/packaging_exclude.php export-ignore
/phpstan.neon export-ignore
/phpstan-baseline.neon export-ignore
/phpunit.functional.xml export-ignore
/phpunit.unit.xml export-ignore
/rector.php export-ignore
/renovate.json export-ignore
* text=auto
/.github export-ignore
/Tests export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.php-cs-fixer.php export-ignore
/CODE_OF_CONDUCT.md export-ignore
/composer.lock export-ignore
/CONTRIBUTING.md export-ignore
/dependency-checker.json export-ignore
/packaging_exclude.php export-ignore
/phpstan.neon export-ignore
/phpstan-baseline.neon export-ignore
/phpunit.functional.xml export-ignore
/phpunit.unit.xml export-ignore
/rector.php export-ignore
/renovate.json export-ignore
3 changes: 2 additions & 1 deletion .github/workflows/cgl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ jobs:

# Check Composer dependencies
- name: Check dependencies
run: composer-require-checker check
# @todo Remove config file once support for TYPO3 v11 and v12 is dropped
run: composer-require-checker check --config-file dependency-checker.json
- name: Re-install Composer dependencies
uses: ramsey/composer-install@v3
- name: Check for unused dependencies
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ jobs:
fail-fast: false
matrix:
php-version: ["8.1", "8.2", "8.3", "8.4"]
typo3-version: ["11.5", "12.4", "13.1"]
typo3-version: ["11.5", "12.4", "13.4"]
dependencies: ["highest", "lowest"]
exclude:
- php-version: "8.1"
typo3-version: "13.1"
typo3-version: "13.4"
- php-version: "8.4"
typo3-version: "11.5"
env:
Expand Down
37 changes: 36 additions & 1 deletion Classes/Cache/Expiration/CacheExpirationCalculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
use TYPO3\CMS\Core\Database\RelationHandler;
use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
use TYPO3\CMS\Core\Schema\TcaSchema;
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
Expand Down Expand Up @@ -164,10 +168,15 @@ public function forRelationHandler(RelationHandler $relationHandler): ?\DateTime

/**
* @param non-empty-string $tableName
* @return array<value-of<EnableField>, non-empty-string|null>
* @return array<value-of<EnableField>, string|null>
*/
protected function getConfiguredEnableFields(string $tableName): array
{
if (\class_exists(TcaSchema::class)) {
return $this->getConfiguredEnableFieldsFromTcaSchema($tableName);
}

// @todo Remove once support for TYPO3 v11 and v12 is dropped
$configuration = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'] ?? [];
$enableFields = [
EnableField::StartTime->value,
Expand All @@ -177,6 +186,32 @@ protected function getConfiguredEnableFields(string $tableName): array
return array_intersect_key($configuration, array_flip($enableFields));
}

/**
* @param non-empty-string $tableName
* @return array<value-of<EnableField>, string|null>
*/
protected function getConfiguredEnableFieldsFromTcaSchema(string $tableName): array
{
// @todo Use DI once support for TYPO3 v11 and v12 is dropped
$tcaSchemaFactory = GeneralUtility::makeInstance(TcaSchemaFactory::class);

// Early return if schema does not exist
if (!$tcaSchemaFactory->has($tableName)) {
return [];
}

$tcaSchema = $tcaSchemaFactory->get($tableName);
$capabilities = [
EnableField::StartTime->value => TcaSchemaCapability::RestrictionStartTime,
EnableField::EndTime->value => TcaSchemaCapability::RestrictionEndTime,
];

return \array_map(
static fn(TcaSchemaCapability $capability) => $tcaSchema->hasCapability($capability) ? $tcaSchema->getCapability($capability)->getFieldName() : null,
$capabilities,
);
}

protected function calculateExpirationDate(
\DateTimeInterface|int|null $startTime,
\DateTimeInterface|int|null $endTime,
Expand Down
59 changes: 59 additions & 0 deletions Classes/Cache/Expiration/CacheLifetimeCalculator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

/*
* This file is part of the TYPO3 CMS extension "cache_bags".
*
* Copyright (C) 2024 Elias Häußler <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

namespace CPSIT\Typo3CacheBags\Cache\Expiration;

use CPSIT\Typo3CacheBags\Cache\Bag\CacheBag;
use TYPO3\CMS\Core\Context\Context;

/**
* CacheLifetimeCalculator
*
* @author Elias Häußler <[email protected]>
* @license GPL-2.0-or-later
*/
class CacheLifetimeCalculator
{
public function __construct(
protected readonly Context $context,
) {}

public function forCacheBag(CacheBag $cacheBag): ?int
{
$expirationDate = $cacheBag->getExpirationDate();

if ($expirationDate !== null) {
return $this->forExpirationDate($expirationDate);
}

return null;
}

public function forExpirationDate(\DateTimeInterface $expirationDate): int
{
/** @var non-negative-int $now */
$now = $this->context->getPropertyFromAspect('date', 'accessTime', 0);

return \max(0, $expirationDate->getTimestamp() - $now);
}
}
35 changes: 34 additions & 1 deletion Classes/EventListener/PageCacheBagRegisteredEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@

namespace CPSIT\Typo3CacheBags\EventListener;

use CPSIT\Typo3CacheBags\Cache\Bag\CacheBag;
use CPSIT\Typo3CacheBags\Cache\Expiration\CacheLifetimeCalculator;
use CPSIT\Typo3CacheBags\Enum\CacheScope;
use CPSIT\Typo3CacheBags\Event\CacheBagRegisteredEvent;
use CPSIT\Typo3CacheBags\Helper\FrontendHelper;
use TYPO3\CMS\Core\Cache\CacheDataCollector;
use TYPO3\CMS\Core\Cache\CacheTag;

/**
* PageCacheBagRegisteredEventListener
Expand All @@ -35,10 +39,39 @@
*/
final class PageCacheBagRegisteredEventListener
{
public function __construct(
private readonly CacheLifetimeCalculator $cacheLifetimeCalculator,
) {}

public function __invoke(CacheBagRegisteredEvent $event): void
{
if ($event->cacheBag->getScope() === CacheScope::Pages) {
FrontendHelper::getTypoScriptFrontendController()->addCacheTags($event->cacheBag->getCacheTags());
$this->addCacheTags($event->cacheBag);
}
}

private function addCacheTags(CacheBag $cacheBag): void
{
if (\class_exists(CacheDataCollector::class)) {
/** @var CacheDataCollector $cacheDataCollector */
$cacheDataCollector = FrontendHelper::getServerRequest()->getAttribute('frontend.cache.collector');
$cacheDataCollector->addCacheTags(...$this->convertCacheTagsToObjects($cacheBag));
} else {
// @todo Remove once support for TYPO3 v11 and v12 is dropped
FrontendHelper::getTypoScriptFrontendController()->addCacheTags($cacheBag->getCacheTags());
}
}

/**
* @return list<CacheTag>
*/
private function convertCacheTagsToObjects(CacheBag $cacheBag): array
{
$lifetime = $this->cacheLifetimeCalculator->forCacheBag($cacheBag) ?? PHP_INT_MAX;

return \array_map(
static fn(string $cacheTag) => new CacheTag($cacheTag, $lifetime),
$cacheBag->getCacheTags(),
);
}
}
16 changes: 11 additions & 5 deletions Classes/EventListener/PageCacheLifetimeEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,38 @@
namespace CPSIT\Typo3CacheBags\EventListener;

use CPSIT\Typo3CacheBags\Cache\Bag\CacheBagRegistry;
use CPSIT\Typo3CacheBags\Cache\Expiration\CacheLifetimeCalculator;
use CPSIT\Typo3CacheBags\Enum\CacheScope;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Cache\CacheDataCollector;
use TYPO3\CMS\Frontend\Event\ModifyCacheLifetimeForPageEvent;

/**
* PageCacheLifetimeEventListener
*
* @author Elias Häußler <[email protected]>
* @license GPL-2.0-or-later
*
* @todo Remove once support for TYPO3 v11 and v12 is dropped
*/
final class PageCacheLifetimeEventListener
{
public function __construct(
private readonly Context $context,
private readonly CacheBagRegistry $cacheBagRegistry,
private readonly CacheLifetimeCalculator $cacheLifetimeCalculator,
) {}

public function __invoke(ModifyCacheLifetimeForPageEvent $event): void
{
// Lifetime modification for TYPO3 >= 13.4 is already done when cache bags are registered
if (class_exists(CacheDataCollector::class)) {
return;
}

$expirationDate = $this->cacheBagRegistry->getExpirationDate(CacheScope::Pages);
/** @var non-negative-int $now */
$now = $this->context->getPropertyFromAspect('date', 'accessTime', 0);

if ($expirationDate !== null) {
$event->setCacheLifetime(
\max(0, $expirationDate->getTimestamp() - $now)
$this->cacheLifetimeCalculator->forExpirationDate($expirationDate),
);
}
}
Expand Down
12 changes: 12 additions & 0 deletions Classes/Helper/FrontendHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
namespace CPSIT\Typo3CacheBags\Helper;

use CPSIT\Typo3CacheBags\Exception\FrontendIsNotInitialized;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;

/**
Expand All @@ -44,4 +45,15 @@ public static function getTypoScriptFrontendController(): TypoScriptFrontendCont

return $typoScriptFrontendController;
}

public static function getServerRequest(): ServerRequestInterface
{
$serverRequest = $GLOBALS['TYPO3_REQUEST'] ?? null;

if (!($serverRequest instanceof ServerRequestInterface)) {
throw new FrontendIsNotInitialized();
}

return $serverRequest;
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ database contents or short-living API requests.
* Cache bag registry to handle generated cache bags
* Cache expiration calculator for various use cases (query builder, query result etc.)
* Event listener to override page cache expiration, based on registered page cache bags
* Compatible with TYPO3 11.5 LTS, 12.4 LTS and 13.1
* Compatible with TYPO3 11.5 LTS, 12.4 LTS and 13.4 LTS

## 🔥 Installation

Expand Down
9 changes: 5 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
"require": {
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"psr/event-dispatcher": "^1.0",
"typo3/cms-core": "~11.5.0 || ~12.4.0 || ~13.1.0",
"typo3/cms-extbase": "~11.5.0 || ~12.4.0 || ~13.1.0",
"typo3/cms-frontend": "~11.5.0 || ~12.4.0 || ~13.1.0"
"psr/http-message": "^1.0 || ^2.0",
"typo3/cms-core": "~11.5.0 || ~12.4.0 || ~13.4.0",
"typo3/cms-extbase": "~11.5.0 || ~12.4.0 || ~13.4.0",
"typo3/cms-frontend": "~11.5.0 || ~12.4.0 || ~13.4.0"
},
"require-dev": {
"armin/editorconfig-cli": "^1.8 || ^2.0",
Expand All @@ -30,7 +31,7 @@
"saschaegerer/phpstan-typo3": "^1.10",
"ssch/typo3-rector": "^2.6",
"typo3/coding-standards": "^0.7.0 || ^0.8.0",
"typo3/testing-framework": "^7.0.2 || ^8.0.9"
"typo3/testing-framework": "^7.0.2 || ^8.0.9 || ^9.0.1"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions dependency-checker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"symbol-whitelist": [
"TYPO3\\CMS\\Core\\Cache\\CacheDataCollector",
"TYPO3\\CMS\\Core\\Cache\\CacheTag",
"TYPO3\\CMS\\Core\\Schema\\Capability\\TcaSchemaCapability",
"TYPO3\\CMS\\Core\\Schema\\TcaSchema",
"TYPO3\\CMS\\Core\\Schema\\TcaSchemaFactory"
]
}
2 changes: 1 addition & 1 deletion ext_emconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
'author_company' => 'coding. powerful. systems. CPS GmbH',
'constraints' => [
'depends' => [
'typo3' => '11.5.0-13.1.99',
'typo3' => '11.5.0-13.4.99',
'php' => '8.1.0-8.4.99',
],
],
Expand Down
1 change: 1 addition & 0 deletions packaging_exclude.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
'CODE_OF_CONDUCT.md',
'composer.lock',
'CONTRIBUTING.md',
'dependency-checker.json',
'editorconfig',
'gitattributes',
'gitignore',
Expand Down
Loading

0 comments on commit a293770

Please sign in to comment.