From b8177562460a5c7a6fd5a02f818a79f9bd83d707 Mon Sep 17 00:00:00 2001 From: Georg Ringer Date: Tue, 2 Dec 2025 12:29:01 +0100 Subject: [PATCH 1/5] [TASK] Allow v14 --- .github/workflows/ci.yml | 10 +++------- composer.json | 8 ++++---- ext_emconf.php | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8e46c6..97e1cc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,13 +13,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [ '7.4', '8.0', '8.1' ] - TYPO3: [ '11' ] - include: - - TYPO3: '12' - php: '8.1' - - TYPO3: '13' - php: '8.3' + php: [ '8.2', '8.3' ] + TYPO3: [ '12', '13', '14' ] + steps: - name: Checkout uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index 88b515d..265dc4f 100644 --- a/composer.json +++ b/composer.json @@ -4,12 +4,12 @@ "license": "GPL-2.0-or-later", "description": "Easy and fast menus for TYPO3 Frontends", "require": { - "typo3/cms-core": "^11 || ^12.4 || ^13.1", - "typo3/cms-frontend": "^11 || ^12.4 || ^13.1" + "typo3/cms-core": "^11 || ^12.4 || ^13.1 || ^14", + "typo3/cms-frontend": "^11 || ^12.4 || ^13.1 || ^14" }, "require-dev": { - "typo3/cms-fluid-styled-content": "^11 || ^12.4 || ^13.1", - "typo3/cms-install": "^11 || ^12.4 || ^13.1", + "typo3/cms-fluid-styled-content": "^11 || ^12.4 || ^13.1 || ^14", + "typo3/cms-install": "^11 || ^12.4 || ^13.1 || ^14", "typo3/coding-standards": "^0.5.5", "saschaegerer/phpstan-typo3": "^1.8", "typo3/tailor": "^1.0", diff --git a/ext_emconf.php b/ext_emconf.php index ef06585..d0b2318 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -11,7 +11,7 @@ 'author_company' => 'b13 GmbH', 'constraints' => [ 'depends' => [ - 'typo3' => '11.5.0-13.4.99', + 'typo3' => '12.4.0-14.4.99', ], ], ]; From 4357b227256485e5e04facb89fa2e4bfb4ba740a Mon Sep 17 00:00:00 2001 From: Georg Ringer Date: Tue, 2 Dec 2025 12:30:14 +0100 Subject: [PATCH 2/5] [TASK] Add phpstan for 14 --- Build/phpstan14.neon | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Build/phpstan14.neon diff --git a/Build/phpstan14.neon b/Build/phpstan14.neon new file mode 100644 index 0000000..e396ce1 --- /dev/null +++ b/Build/phpstan14.neon @@ -0,0 +1,27 @@ +parameters: + level: 5 + + paths: + - %currentWorkingDirectory%/Classes + - %currentWorkingDirectory%/Tests + excludePaths: + - %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php + ignoreErrors: + - + message: '#Call to an undefined method TYPO3\\CMS\\Core\\Site\\Entity\\SiteLanguage::getTwoLetterIsoCode\(\).#' + path: %currentWorkingDirectory%/Classes/Compiler/LanguageMenuCompiler.php + - + message: '#Access to undefined constant TYPO3\\CMS\\Core\\Domain\\Repository\\PageRepository::DOKTYPE_RECYCLER.#' + path: %currentWorkingDirectory%/Classes/Domain/Repository/MenuRepository.php + - + message: '#Access to undefined constant TYPO3\\CMS\\Core\\Domain\\Repository\\PageRepository::DOKTYPE_RECYCLER.#' + path: %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php + - + message: '#.*get_cache_timeout\(\).*#' + path: %currentWorkingDirectory%/Classes/CacheHelper.php + - + message: '#Call to an undefined static method TYPO3\\CMS\\Frontend\\ContentObject\\AbstractContentObject::__construct\(\).#' + path: %currentWorkingDirectory%/Classes/ContentObject/* + - + message: '#Property TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController::\$id \(int\) does not accept string.#' + path: %currentWorkingDirectory%/Tests/Functional/Compiler/LanguageMenuCompilerTest.php From 6386f8cf01d1be8e1ee2f2a654a5b9099a9bb48e Mon Sep 17 00:00:00 2001 From: Achim Fritz Date: Thu, 4 Dec 2025 17:44:41 +0100 Subject: [PATCH 3/5] [TASK] runTests for v14 --- Build/testing-docker/docker-compose.yml | 3 +++ composer.json | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml index 3cf5d8b..1a4a3dd 100644 --- a/Build/testing-docker/docker-compose.yml +++ b/Build/testing-docker/docker-compose.yml @@ -88,6 +88,9 @@ services: elif [ ${TYPO3} -eq 12 ]; then composer require typo3/cms-install:^12.4 typo3/cms-fluid-styled-content:^12.4 --dev -W --no-progress --no-interaction composer prepare-tests + elif [ ${TYPO3} -eq 14 ]; then + composer require typo3/cms-install:^14 typo3/cms-fluid-styled-content:^14 --dev -W --no-progress --no-interaction + composer prepare-tests else composer install --no-progress --no-interaction composer prepare-tests diff --git a/composer.json b/composer.json index 265dc4f..916afc2 100644 --- a/composer.json +++ b/composer.json @@ -11,10 +11,10 @@ "typo3/cms-fluid-styled-content": "^11 || ^12.4 || ^13.1 || ^14", "typo3/cms-install": "^11 || ^12.4 || ^13.1 || ^14", "typo3/coding-standards": "^0.5.5", - "saschaegerer/phpstan-typo3": "^1.8", + "phpstan/phpstan": "^1.10", "typo3/tailor": "^1.0", - "typo3/testing-framework": "^7.0 || ^8.0", - "phpunit/phpunit": "9.6 || ^10.5" + "typo3/testing-framework": "^7.0 || ^8.0 || ^9.3", + "phpunit/phpunit": "9.6 || ^10.5 || ^11.3" }, "config": { "vendor-dir": ".Build/vendor", From 70e9e8908be9270c369860b73b38aa8d940fbbcf Mon Sep 17 00:00:00 2001 From: Achim Fritz Date: Fri, 9 Jan 2026 16:35:44 +0100 Subject: [PATCH 4/5] [TASK] first run on v14 --- Classes/CacheHelper.php | 34 ++++++++++-------- Classes/Compiler/LanguageMenuCompiler.php | 14 +++++++- .../BreadcrumbsContentObject.php | 15 +++++++- Classes/DataProcessing/BreadcrumbsMenu.php | 16 ++++++++- Classes/PageStateMarker.php | 36 ++++++++++++++++--- 5 files changed, 94 insertions(+), 21 deletions(-) diff --git a/Classes/CacheHelper.php b/Classes/CacheHelper.php index 4eca271..cb9764c 100644 --- a/Classes/CacheHelper.php +++ b/Classes/CacheHelper.php @@ -17,7 +17,6 @@ use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException; use TYPO3\CMS\Core\Information\Typo3Version; -use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\TypoScript\FrontendTypoScript; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\Cache\CacheLifetimeCalculator; @@ -29,7 +28,7 @@ * The pure joy of this class is the get() method, which calculates tags and max lifetime based on the fetched * records. If found in cache, fetched directly. */ -class CacheHelper implements SingletonInterface +class CacheHelper { protected FrontendInterface $cache; protected bool $disableCaching = false; @@ -126,17 +125,18 @@ protected function getAllPageIdsFromItems(array $pages): array protected function getDefaultMaxLifeTime(): int { if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() < 13) { - $maxLifetime = (int)$this->getFrontendController()->get_cache_timeout(); - } else { - $request = $this->getServerRequest(); - $pageInformation = $request->getAttribute('frontend.page.information'); - /** @var ?FrontendTypoScript $typoScript */ - $typoScript = $request->getAttribute('frontend.typoscript'); - if ($typoScript === null || $pageInformation === null) { - return 0; - } - $typoScriptConfigArray = $typoScript->getConfigArray(); - $maxLifetime = GeneralUtility::makeInstance(CacheLifetimeCalculator::class) + return (int)$this->getFrontendController()->get_cache_timeout(); + } + $request = $this->getServerRequest(); + $pageInformation = $request->getAttribute('frontend.page.information'); + /** @var ?FrontendTypoScript $typoScript */ + $typoScript = $request->getAttribute('frontend.typoscript'); + if ($typoScript === null || $pageInformation === null) { + return 0; + } + $typoScriptConfigArray = $typoScript->getConfigArray(); + if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() < 14) { + return GeneralUtility::makeInstance(CacheLifetimeCalculator::class) ->calculateLifetimeForPage( $pageInformation->getId(), $pageInformation->getPageRecord(), @@ -145,7 +145,13 @@ protected function getDefaultMaxLifeTime(): int $this->context ); } - return $maxLifetime; + return GeneralUtility::makeInstance(CacheLifetimeCalculator::class) + ->calculateLifetimeForPage( + $pageInformation->getId(), + $pageInformation->getPageRecord(), + $typoScriptConfigArray, + $this->context + ); } /** diff --git a/Classes/Compiler/LanguageMenuCompiler.php b/Classes/Compiler/LanguageMenuCompiler.php index 27c1725..3acf11e 100644 --- a/Classes/Compiler/LanguageMenuCompiler.php +++ b/Classes/Compiler/LanguageMenuCompiler.php @@ -17,6 +17,7 @@ use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageInformation; class LanguageMenuCompiler extends AbstractMenuCompiler { @@ -29,7 +30,7 @@ public function compile(ContentObjectRenderer $contentObjectRenderer, array $con $excludedLanguages = $contentObjectRenderer->stdWrap($configuration['excludeLanguages'] ?? '', $configuration['excludeLanguages.'] ?? []); $excludedLanguages = GeneralUtility::trimExplode(',', $excludedLanguages, true); - $targetPage = $contentObjectRenderer->stdWrap($configuration['pointToPage'] ?? $GLOBALS['TSFE']->id, $configuration['pointToPage.'] ?? []); + $targetPage = $contentObjectRenderer->stdWrap($configuration['pointToPage'] ?? $this->getCurrentPageId($contentObjectRenderer), $configuration['pointToPage.'] ?? []); $targetPage = (int)$targetPage; $addAllSiteLanguages = isset($configuration['addAllSiteLanguages']) && (bool)$configuration['addAllSiteLanguages'] === true; @@ -65,6 +66,17 @@ public function compile(ContentObjectRenderer $contentObjectRenderer, array $con }); } + protected function getCurrentPageId(ContentObjectRenderer $cObj): int + { + if ((new Typo3Version())->getMajorVersion() < 13) { + return $GLOBALS['TSFE']->id; + } + $request = $cObj->getRequest(); + /** @var PageInformation $pageInformation */ + $pageInformation = $request->getAttribute('frontend.page.information'); + return $pageInformation->getId(); + } + protected function getLanguageCode(SiteLanguage $language): string { if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() === 11) { diff --git a/Classes/ContentObject/BreadcrumbsContentObject.php b/Classes/ContentObject/BreadcrumbsContentObject.php index 5038c9c..eb61860 100644 --- a/Classes/ContentObject/BreadcrumbsContentObject.php +++ b/Classes/ContentObject/BreadcrumbsContentObject.php @@ -17,6 +17,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageInformation; /** * Build a breadcrumbs navigation, no caching involved. @@ -39,7 +40,8 @@ public function __construct(ContentObjectRenderer $cObj) */ public function render($conf = []) { - $pages = $this->menuRepository->getBreadcrumbsMenu($GLOBALS['TSFE']->rootLine, $conf); + $rootLine = $this->getRootline(); + $pages = $this->menuRepository->getBreadcrumbsMenu($rootLine, $conf); $content = ''; $cObjForItems = GeneralUtility::makeInstance(ContentObjectRenderer::class); $rootLevelCount = count($pages); @@ -50,4 +52,15 @@ public function render($conf = []) } return $this->cObj->stdWrap($content, $conf); } + + protected function getRootline(): array + { + if ((new Typo3Version())->getMajorVersion() < 13) { + return $GLOBALS['TSFE']->rootLine; + } + $request = $this->cObj->getRequest(); + /** @var PageInformation $pageInformation */ + $pageInformation = $request->getAttribute('frontend.page.information'); + return $pageInformation->getRootLine(); + } } diff --git a/Classes/DataProcessing/BreadcrumbsMenu.php b/Classes/DataProcessing/BreadcrumbsMenu.php index ae5a85a..24c47fd 100644 --- a/Classes/DataProcessing/BreadcrumbsMenu.php +++ b/Classes/DataProcessing/BreadcrumbsMenu.php @@ -13,8 +13,10 @@ use B13\Menus\Domain\Repository\MenuRepository; use B13\Menus\PageStateMarker; +use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageInformation; /** * DataProcessor to retrieve a list of all pages of the current rootline to build a breadcrumb menu. @@ -37,7 +39,8 @@ public function process(ContentObjectRenderer $cObj, array $contentObjectConfigu if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { return $processedData; } - $pages = $this->menuRepository->getBreadcrumbsMenu($GLOBALS['TSFE']->rootLine, $processorConfiguration); + $rootLine = $this->getRootline($cObj); + $pages = $this->menuRepository->getBreadcrumbsMenu($rootLine, $processorConfiguration); $rootLevelCount = count($pages); foreach ($pages as &$page) { PageStateMarker::markStates($page, $rootLevelCount--); @@ -49,4 +52,15 @@ public function process(ContentObjectRenderer $cObj, array $contentObjectConfigu $processedData[$targetVariableName] = $pages; return $processedData; } + + protected function getRootline(ContentObjectRenderer $cObj): array + { + if ((new Typo3Version())->getMajorVersion() < 13) { + return $GLOBALS['TSFE']->rootLine; + } + $request = $cObj->getRequest(); + /** @var PageInformation $pageInformation */ + $pageInformation = $request->getAttribute('frontend.page.information'); + return $pageInformation->getRootLine(); + } } diff --git a/Classes/PageStateMarker.php b/Classes/PageStateMarker.php index f7769b4..7dbd8d0 100644 --- a/Classes/PageStateMarker.php +++ b/Classes/PageStateMarker.php @@ -11,6 +11,10 @@ * of the License, or any later version. */ +use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Core\Information\Typo3Version; +use TYPO3\CMS\Frontend\Page\PageInformation; + /** * Helper class to set additional properties for a page */ @@ -43,15 +47,39 @@ public static function markStates(array &$page, ?int $level = null): void private static function isPageInCurrentRootLine(int $pageId): bool { - if (!is_array($GLOBALS['TSFE']->rootLine)) { + if ((new Typo3Version())->getMajorVersion() < 13) { + if (!is_array($GLOBALS['TSFE']->rootLine)) { + return false; + } + return in_array($pageId, array_column($GLOBALS['TSFE']->rootLine, 'uid')); + } + $pageInformation = self::getPageInformationFromRequest(); + if ($pageInformation === null) { return false; } - - return in_array($pageId, array_column($GLOBALS['TSFE']->rootLine, 'uid')); + return in_array($pageId, array_column($pageInformation->getRootLine(), 'uid'), true); } private static function isCurrentPage(int $pageId): bool { - return $pageId === $GLOBALS['TSFE']->id; + if ((new Typo3Version())->getMajorVersion() < 13) { + return $pageId === $GLOBALS['TSFE']->id; + } + $pageInformation = self::getPageInformationFromRequest(); + if ($pageInformation === null) { + return false; + } + return $pageInformation->getId() === $pageId; + } + + private static function getPageInformationFromRequest(): ?PageInformation + { + $request = $GLOBALS['TSFE'] ?? null; + if ($request instanceof ServerRequestInterface) { + /** @var ?PageInformation $pageInformation */ + $pageInformation = $request->getAttribute('frontend.page.information'); + return $pageInformation; + } + return null; } } From 4ffe5d0015e0de5c812f600b737d9d136fb76184 Mon Sep 17 00:00:00 2001 From: Achim Fritz Date: Mon, 12 Jan 2026 08:15:43 +0100 Subject: [PATCH 5/5] [TASK] v14 ci --- Build/Scripts/runTests.sh | 14 +-- Build/php-cs-fixer.php | 6 ++ Build/phpstan-baseline12.neon | 91 +++++++++++++++++++ Build/phpstan-baseline13.neon | 51 +++++++++++ Build/phpstan-baseline14.neon | 56 ++++++++++++ Build/phpstan11.neon | 3 - Build/phpstan12.neon | 19 +--- Build/phpstan13.neon | 24 +---- Build/phpstan14.neon | 24 +---- Build/testing-docker/docker-compose.yml | 9 +- Classes/Hooks/DataHandlerHook.php | 7 +- Classes/PageStateMarker.php | 2 +- .../Compiler/LanguageMenuCompilerTest.php | 29 ++++-- .../DataProcessing/BreadcrumbsMenuTest.php | 20 +++- .../DataProcessing/ListMenuProcessorTest.php | 33 +++++-- .../DataProcessing/TreeMenuProcessorTest.php | 32 +++++-- .../DataProcessing/BreadcrumbsMenuTest.php | 24 ++++- .../Domain/Repository/MenuRepositoryTest.php | 9 +- Tests/Unit/PageStateMarkerTest.php | 35 +++++-- ext_emconf.php | 2 +- 20 files changed, 366 insertions(+), 124 deletions(-) create mode 100644 Build/phpstan-baseline12.neon create mode 100644 Build/phpstan-baseline13.neon create mode 100644 Build/phpstan-baseline14.neon diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index bed0ddb..a31b60d 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -50,9 +50,9 @@ Options: - postgres: use postgres - sqlite: use sqlite - -p <7.2|7.3|7.4|8.0|8.1> + -p <8.1|8.2|8.3|8.4> Specifies the PHP minor version to be used - - 7.4 (default): use PHP 7.4 + - 8.3 (default): use PHP 8.3 -e "" Only with -s acceptance|functional|unit @@ -88,11 +88,11 @@ Options: Show this help. Examples: - # Run unit tests using PHP 7.4 + # Run unit tests using PHP 8.4 ./Build/Scripts/runTests.sh - # Run unit tests using PHP 7.3 - ./Build/Scripts/runTests.sh -p 7.3 + # Run unit tests using PHP 8.3 + ./Build/Scripts/runTests.sh -p 8.3 EOF # Go to the directory this script is located, so everything else is relative @@ -106,12 +106,12 @@ cd ../testing-docker || exit 1 ROOT_DIR=`readlink -f ${PWD}/../../` TEST_SUITE="unit" DBMS="mariadb" -PHP_VERSION="7.4" +PHP_VERSION="8.3" PHP_XDEBUG_ON=0 PHP_XDEBUG_PORT=9000 EXTRA_TEST_OPTIONS="" SCRIPT_VERBOSE=0 -TYPO3="10" +TYPO3="13" # Option parsing # Reset in case getopts has been used previously in the shell diff --git a/Build/php-cs-fixer.php b/Build/php-cs-fixer.php index 6ee0307..16494b6 100644 --- a/Build/php-cs-fixer.php +++ b/Build/php-cs-fixer.php @@ -2,4 +2,10 @@ $config = \TYPO3\CodingStandards\CsFixerConfig::create(); $config->getFinder()->exclude(['var'])->in(__DIR__ . '/..'); +$config->addRules([ + 'nullable_type_declaration' => [ + 'syntax' => 'question_mark', + ], + 'nullable_type_declaration_for_default_null_value' => true, +]); return $config; diff --git a/Build/phpstan-baseline12.neon b/Build/phpstan-baseline12.neon new file mode 100644 index 0000000..3ca2272 --- /dev/null +++ b/Build/phpstan-baseline12.neon @@ -0,0 +1,91 @@ +parameters: + ignoreErrors: + - + message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Core\\\\TypoScript\\\\FrontendTypoScript\\:\\:getConfigArray\\(\\)\\.$#" + count: 2 + path: ../Classes/CacheHelper.php + + - + message: "#^Instantiated class TYPO3\\\\CMS\\\\Core\\\\Cache\\\\CacheTag not found\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Method TYPO3\\\\CMS\\\\Frontend\\\\Cache\\\\CacheLifetimeCalculator\\:\\:calculateLifetimeForPage\\(\\) invoked with 4 parameters, 5 required\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Parameter \\#4 \\$defaultCacheTimoutInSeconds of method TYPO3\\\\CMS\\\\Frontend\\\\Cache\\\\CacheLifetimeCalculator\\:\\:calculateLifetimeForPage\\(\\) expects int, TYPO3\\\\CMS\\\\Core\\\\Context\\\\Context given\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Call to method getId\\(\\) on an unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/Compiler/LanguageMenuCompiler.php + + - + message: "#^PHPDoc tag @var for variable \\$pageInformation contains unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/Compiler/LanguageMenuCompiler.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/BreadcrumbsContentObject.php + + - + message: "#^Call to method getRootLine\\(\\) on an unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/ContentObject/BreadcrumbsContentObject.php + + - + message: "#^PHPDoc tag @var for variable \\$pageInformation contains unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/ContentObject/BreadcrumbsContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/LanguageMenuContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/ListMenuContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/TreeMenuContentObject.php + + - + message: "#^Call to method getRootLine\\(\\) on an unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/DataProcessing/BreadcrumbsMenu.php + + - + message: "#^PHPDoc tag @var for variable \\$pageInformation contains unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/DataProcessing/BreadcrumbsMenu.php + + - + message: "#^Call to method getId\\(\\) on an unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/PageStateMarker.php + + - + message: "#^Call to method getRootLine\\(\\) on an unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/PageStateMarker.php + + - + message: "#^Method B13\\\\Menus\\\\PageStateMarker\\:\\:getPageInformationFromRequest\\(\\) has invalid return type TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/PageStateMarker.php + + - + message: "#^PHPDoc tag @var for variable \\$pageInformation contains unknown class TYPO3\\\\CMS\\\\Frontend\\\\Page\\\\PageInformation\\.$#" + count: 1 + path: ../Classes/PageStateMarker.php diff --git a/Build/phpstan-baseline13.neon b/Build/phpstan-baseline13.neon new file mode 100644 index 0000000..c13049e --- /dev/null +++ b/Build/phpstan-baseline13.neon @@ -0,0 +1,51 @@ +parameters: + ignoreErrors: + - + message: "#^Call to protected method get_cache_timeout\\(\\) of class TYPO3\\\\CMS\\\\Frontend\\\\Controller\\\\TypoScriptFrontendController\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Method TYPO3\\\\CMS\\\\Frontend\\\\Cache\\\\CacheLifetimeCalculator\\:\\:calculateLifetimeForPage\\(\\) invoked with 4 parameters, 5 required\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Method TYPO3\\\\CMS\\\\Frontend\\\\Controller\\\\TypoScriptFrontendController\\:\\:get_cache_timeout\\(\\) invoked with 0 parameters, 1 required\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Parameter \\#4 \\$defaultCacheTimoutInSeconds of method TYPO3\\\\CMS\\\\Frontend\\\\Cache\\\\CacheLifetimeCalculator\\:\\:calculateLifetimeForPage\\(\\) expects int, TYPO3\\\\CMS\\\\Core\\\\Context\\\\Context given\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Core\\\\Site\\\\Entity\\\\SiteLanguage\\:\\:getTwoLetterIsoCode\\(\\)\\.$#" + count: 1 + path: ../Classes/Compiler/LanguageMenuCompiler.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/BreadcrumbsContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/LanguageMenuContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/ListMenuContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/TreeMenuContentObject.php + + - + message: "#^Access to undefined constant TYPO3\\\\CMS\\\\Core\\\\Domain\\\\Repository\\\\PageRepository\\:\\:DOKTYPE_RECYCLER\\.$#" + count: 1 + path: ../Classes/Domain/Repository/MenuRepository.php diff --git a/Build/phpstan-baseline14.neon b/Build/phpstan-baseline14.neon new file mode 100644 index 0000000..c341a9e --- /dev/null +++ b/Build/phpstan-baseline14.neon @@ -0,0 +1,56 @@ +parameters: + ignoreErrors: + - + message: "#^Call to method addCacheTags\\(\\) on an unknown class TYPO3\\\\CMS\\\\Frontend\\\\Controller\\\\TypoScriptFrontendController\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Call to method get_cache_timeout\\(\\) on an unknown class TYPO3\\\\CMS\\\\Frontend\\\\Controller\\\\TypoScriptFrontendController\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Method B13\\\\Menus\\\\CacheHelper\\:\\:getFrontendController\\(\\) has invalid return type TYPO3\\\\CMS\\\\Frontend\\\\Controller\\\\TypoScriptFrontendController\\.$#" + count: 1 + path: ../Classes/CacheHelper.php + + - + message: "#^Method TYPO3\\\\CMS\\\\Frontend\\\\Cache\\\\CacheLifetimeCalculator\\:\\:calculateLifetimeForPage\\(\\) invoked with 5 parameters, 4 required\\.$#" + count: 2 + path: ../Classes/CacheHelper.php + + - + message: "#^Parameter \\#4 \\$context of method TYPO3\\\\CMS\\\\Frontend\\\\Cache\\\\CacheLifetimeCalculator\\:\\:calculateLifetimeForPage\\(\\) expects TYPO3\\\\CMS\\\\Core\\\\Context\\\\Context, int given\\.$#" + count: 2 + path: ../Classes/CacheHelper.php + + - + message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Core\\\\Site\\\\Entity\\\\SiteLanguage\\:\\:getTwoLetterIsoCode\\(\\)\\.$#" + count: 1 + path: ../Classes/Compiler/LanguageMenuCompiler.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/BreadcrumbsContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/LanguageMenuContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/ListMenuContentObject.php + + - + message: "#^Call to an undefined static method TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\AbstractContentObject\\:\\:__construct\\(\\)\\.$#" + count: 1 + path: ../Classes/ContentObject/TreeMenuContentObject.php + + - + message: "#^Access to undefined constant TYPO3\\\\CMS\\\\Core\\\\Domain\\\\Repository\\\\PageRepository\\:\\:DOKTYPE_RECYCLER\\.$#" + count: 1 + path: ../Classes/Domain/Repository/MenuRepository.php diff --git a/Build/phpstan11.neon b/Build/phpstan11.neon index 3f5dd7c..3b9a1d9 100644 --- a/Build/phpstan11.neon +++ b/Build/phpstan11.neon @@ -3,9 +3,6 @@ parameters: paths: - %currentWorkingDirectory%/Classes - - %currentWorkingDirectory%/Tests - excludePaths: - - %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php ignoreErrors: - message: '#Cannot call method getLanguageCode\(\) on string.#' diff --git a/Build/phpstan12.neon b/Build/phpstan12.neon index bbf975e..2929379 100644 --- a/Build/phpstan12.neon +++ b/Build/phpstan12.neon @@ -1,22 +1,7 @@ +includes: + - phpstan-baseline12.neon parameters: level: 5 paths: - %currentWorkingDirectory%/Classes - - %currentWorkingDirectory%/Tests - excludePaths: - - %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php - ignoreErrors: - - - message: '#Call to an undefined static method TYPO3\\CMS\\Frontend\\ContentObject\\AbstractContentObject::__construct\(\).#' - path: %currentWorkingDirectory%/Classes/ContentObject/* - - - message: '#Property TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController::\$id \(int\) does not accept string.#' - path: %currentWorkingDirectory%/Tests/Functional/Compiler/LanguageMenuCompilerTest.php - - - message: '#Call to an undefined method TYPO3\\CMS\\Core\\TypoScript\\FrontendTypoScript::getConfigArray\(\).#' - path: %currentWorkingDirectory%/Classes/CacheHelper.php - - - message: '#Instantiated class TYPO3\\CMS\\Core\\Cache\\CacheDataCollector not found.#' - - - message: '#Instantiated class TYPO3\\CMS\\Core\\Cache\\CacheTag not found.#' diff --git a/Build/phpstan13.neon b/Build/phpstan13.neon index e396ce1..63a00ed 100644 --- a/Build/phpstan13.neon +++ b/Build/phpstan13.neon @@ -1,27 +1,7 @@ +includes: + - phpstan-baseline13.neon parameters: level: 5 paths: - %currentWorkingDirectory%/Classes - - %currentWorkingDirectory%/Tests - excludePaths: - - %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php - ignoreErrors: - - - message: '#Call to an undefined method TYPO3\\CMS\\Core\\Site\\Entity\\SiteLanguage::getTwoLetterIsoCode\(\).#' - path: %currentWorkingDirectory%/Classes/Compiler/LanguageMenuCompiler.php - - - message: '#Access to undefined constant TYPO3\\CMS\\Core\\Domain\\Repository\\PageRepository::DOKTYPE_RECYCLER.#' - path: %currentWorkingDirectory%/Classes/Domain/Repository/MenuRepository.php - - - message: '#Access to undefined constant TYPO3\\CMS\\Core\\Domain\\Repository\\PageRepository::DOKTYPE_RECYCLER.#' - path: %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php - - - message: '#.*get_cache_timeout\(\).*#' - path: %currentWorkingDirectory%/Classes/CacheHelper.php - - - message: '#Call to an undefined static method TYPO3\\CMS\\Frontend\\ContentObject\\AbstractContentObject::__construct\(\).#' - path: %currentWorkingDirectory%/Classes/ContentObject/* - - - message: '#Property TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController::\$id \(int\) does not accept string.#' - path: %currentWorkingDirectory%/Tests/Functional/Compiler/LanguageMenuCompilerTest.php diff --git a/Build/phpstan14.neon b/Build/phpstan14.neon index e396ce1..ef4eeed 100644 --- a/Build/phpstan14.neon +++ b/Build/phpstan14.neon @@ -1,27 +1,7 @@ +includes: + - phpstan-baseline14.neon parameters: level: 5 paths: - %currentWorkingDirectory%/Classes - - %currentWorkingDirectory%/Tests - excludePaths: - - %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php - ignoreErrors: - - - message: '#Call to an undefined method TYPO3\\CMS\\Core\\Site\\Entity\\SiteLanguage::getTwoLetterIsoCode\(\).#' - path: %currentWorkingDirectory%/Classes/Compiler/LanguageMenuCompiler.php - - - message: '#Access to undefined constant TYPO3\\CMS\\Core\\Domain\\Repository\\PageRepository::DOKTYPE_RECYCLER.#' - path: %currentWorkingDirectory%/Classes/Domain/Repository/MenuRepository.php - - - message: '#Access to undefined constant TYPO3\\CMS\\Core\\Domain\\Repository\\PageRepository::DOKTYPE_RECYCLER.#' - path: %currentWorkingDirectory%/Tests/Unit/Domain/Repository/MenuRepositoryTest.php - - - message: '#.*get_cache_timeout\(\).*#' - path: %currentWorkingDirectory%/Classes/CacheHelper.php - - - message: '#Call to an undefined static method TYPO3\\CMS\\Frontend\\ContentObject\\AbstractContentObject::__construct\(\).#' - path: %currentWorkingDirectory%/Classes/ContentObject/* - - - message: '#Property TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController::\$id \(int\) does not accept string.#' - path: %currentWorkingDirectory%/Tests/Functional/Compiler/LanguageMenuCompilerTest.php diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml index 1a4a3dd..dcad8f4 100644 --- a/Build/testing-docker/docker-compose.yml +++ b/Build/testing-docker/docker-compose.yml @@ -82,14 +82,11 @@ services: set -x fi php -v | grep '^PHP'; - if [ ${TYPO3} -eq 11 ]; then - composer require typo3/cms-install:^11.5 typo3/cms-fluid-styled-content:^11.5 --dev -W --no-progress --no-interaction - composer prepare-tests - elif [ ${TYPO3} -eq 12 ]; then + if [ ${TYPO3} -eq 12 ]; then composer require typo3/cms-install:^12.4 typo3/cms-fluid-styled-content:^12.4 --dev -W --no-progress --no-interaction composer prepare-tests - elif [ ${TYPO3} -eq 14 ]; then - composer require typo3/cms-install:^14 typo3/cms-fluid-styled-content:^14 --dev -W --no-progress --no-interaction + elif [ ${TYPO3} -eq 13 ]; then + composer require typo3/cms-install:^13.4 typo3/cms-fluid-styled-content:^13.4 --dev -W --no-progress --no-interaction composer prepare-tests else composer install --no-progress --no-interaction diff --git a/Classes/Hooks/DataHandlerHook.php b/Classes/Hooks/DataHandlerHook.php index 5589b5e..69d1878 100644 --- a/Classes/Hooks/DataHandlerHook.php +++ b/Classes/Hooks/DataHandlerHook.php @@ -12,6 +12,7 @@ * of the License, or any later version. */ +use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Information\Typo3Version; @@ -40,7 +41,11 @@ public function clearMenuCaches(array $params, DataHandler $dataHandler): void $menuTags = ['menuId_' . $pageId]; // Clear caches of the parent page as well (needed when moving records) - $parentPageId = $dataHandler->getPID('pages', $pageId); + $record = BackendUtility::getRecord('pages', $pageId); + if ($record == null) { + return; + } + $parentPageId = $record['pid'] ?? 0; if ($parentPageId > 0) { $menuTags[] = 'menuId_' . $parentPageId; } diff --git a/Classes/PageStateMarker.php b/Classes/PageStateMarker.php index 7dbd8d0..1735fd1 100644 --- a/Classes/PageStateMarker.php +++ b/Classes/PageStateMarker.php @@ -74,7 +74,7 @@ private static function isCurrentPage(int $pageId): bool private static function getPageInformationFromRequest(): ?PageInformation { - $request = $GLOBALS['TSFE'] ?? null; + $request = $GLOBALS['TYPO3_REQUEST'] ?? null; if ($request instanceof ServerRequestInterface) { /** @var ?PageInformation $pageInformation */ $pageInformation = $request->getAttribute('frontend.page.information'); diff --git a/Tests/Functional/Compiler/LanguageMenuCompilerTest.php b/Tests/Functional/Compiler/LanguageMenuCompilerTest.php index 96c4677..92b6456 100644 --- a/Tests/Functional/Compiler/LanguageMenuCompilerTest.php +++ b/Tests/Functional/Compiler/LanguageMenuCompilerTest.php @@ -19,11 +19,13 @@ use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Domain\Repository\PageRepository; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; +use TYPO3\CMS\Frontend\Page\PageInformation; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; class LanguageMenuCompilerTest extends FunctionalTestCase @@ -239,17 +241,26 @@ protected function compileMenu(array $pageDataset, array $configuration = []): a foreach ($pageDataset as $page) { $connection->insert('pages', $page); } - $controller = $this->getMockBuilder($this->buildAccessibleProxy(TypoScriptFrontendController::class)) - ->onlyMethods(['get_cache_timeout']) - ->disableOriginalConstructor() - ->getMock(); - $GLOBALS['TSFE'] = $controller; - if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() < 12) { - $GLOBALS['TSFE']->id = '1'; - } else { + if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() < 13) { + $controller = $this->getMockBuilder($this->buildAccessibleProxy(TypoScriptFrontendController::class)) + ->onlyMethods(['get_cache_timeout']) + ->disableOriginalConstructor() + ->getMock(); + $GLOBALS['TSFE'] = $controller; $GLOBALS['TSFE']->id = 1; + $contentObjectRenderer = new ContentObjectRenderer(); + } else { + $request = GeneralUtility::makeInstance(ServerRequest::class); + $pageInformation = new PageInformation(); + $pageInformation->setId(1); + $request = $request->withAttribute('frontend.page.information', $pageInformation); + $contentObjectRenderer = $this->getMockBuilder(ContentObjectRenderer::class) + ->disableOriginalConstructor() + ->onlyMethods([]) + ->getMock(); + $contentObjectRenderer->setRequest($request); } - $contentObjectRenderer = new ContentObjectRenderer(); + $siteFinder = GeneralUtility::makeInstance(SiteFinder::class); $site = $siteFinder->getSiteByIdentifier('main'); $context = $this->getMockBuilder(Context::class) diff --git a/Tests/Functional/DataProcessing/BreadcrumbsMenuTest.php b/Tests/Functional/DataProcessing/BreadcrumbsMenuTest.php index f62373a..b68efdf 100644 --- a/Tests/Functional/DataProcessing/BreadcrumbsMenuTest.php +++ b/Tests/Functional/DataProcessing/BreadcrumbsMenuTest.php @@ -13,8 +13,12 @@ */ use B13\Menus\DataProcessing\BreadcrumbsMenu; +use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder; +use TYPO3\CMS\Core\Http\ServerRequest; +use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageInformation; class BreadcrumbsMenuTest extends DataProcessing { @@ -191,9 +195,19 @@ public static function setupDataProvider() */ public function processTest(array $tsfe, array $configuration, array $expected): void { - $GLOBALS['TSFE'] = new \stdClass(); - $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; - $GLOBALS['TSFE']->id = $tsfe['id']; + if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { + $request = GeneralUtility::makeInstance(ServerRequest::class); + $request = $request->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_FE); + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($tsfe['rootLine']); + $pageInformation->setId($tsfe['id']); + $GLOBALS['TYPO3_REQUEST'] = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = new \stdClass(); + $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; + $GLOBALS['TSFE']->id = $tsfe['id']; + } + $breadcrumbsMenuProcessor = GeneralUtility::makeInstance(BreadcrumbsMenu::class); $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); $breadcrumbs = $breadcrumbsMenuProcessor->process($contentObjectRenderer, [], $configuration, []); diff --git a/Tests/Functional/DataProcessing/ListMenuProcessorTest.php b/Tests/Functional/DataProcessing/ListMenuProcessorTest.php index 2bc5fda..5d384c2 100644 --- a/Tests/Functional/DataProcessing/ListMenuProcessorTest.php +++ b/Tests/Functional/DataProcessing/ListMenuProcessorTest.php @@ -14,12 +14,14 @@ use B13\Menus\DataProcessing\ListMenu; use TYPO3\CMS\Core\Cache\CacheDataCollector; +use TYPO3\CMS\Core\Cache\CacheTag; use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder; use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageInformation; class ListMenuProcessorTest extends DataProcessing { @@ -322,13 +324,17 @@ public function processTest(array $tsfe, array $configuration, array $expected) if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { $cacheDataCollector = new CacheDataCollector(); $request = $request->withAttribute('frontend.cache.collector', $cacheDataCollector); + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($tsfe['rootLine']); + $pageInformation->setId($tsfe['id']); + $request = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); + $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; + $GLOBALS['TSFE']->id = $tsfe['id']; } $GLOBALS['TYPO3_REQUEST'] = $request; - $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); - $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; - $GLOBALS['TSFE']->id = $tsfe['id']; - $listMenuProcessor = GeneralUtility::makeInstance(ListMenu::class); $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); $listMenu = $listMenuProcessor->process($contentObjectRenderer, [], $configuration, []); @@ -380,17 +386,24 @@ public function menuIdTagsAreAddedToPageCache(array $tsfe, array $configuration, if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { $cacheDataCollector = new CacheDataCollector(); $request = $request->withAttribute('frontend.cache.collector', $cacheDataCollector); + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($tsfe['rootLine']); + $pageInformation->setId($tsfe['id']); + $request = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); + $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; + $GLOBALS['TSFE']->id = $tsfe['id']; } $GLOBALS['TYPO3_REQUEST'] = $request; - - $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); - $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; - $GLOBALS['TSFE']->id = $tsfe['id']; - $listMenuProcessor = GeneralUtility::makeInstance(ListMenu::class); $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); $listMenuProcessor->process($contentObjectRenderer, [], $configuration, []); - $pageCacheTags = $GLOBALS['TSFE']->getPageCacheTags(); + if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { + $pageCacheTags = array_map(fn (CacheTag $cacheTag) => $cacheTag->name, $cacheDataCollector->getCacheTags()); + } else { + $pageCacheTags = $GLOBALS['TSFE']->getPageCacheTags(); + } self::assertSame(count($expectedTags), count($pageCacheTags)); foreach ($expectedTags as $expectedTag) { diff --git a/Tests/Functional/DataProcessing/TreeMenuProcessorTest.php b/Tests/Functional/DataProcessing/TreeMenuProcessorTest.php index b84bb80..85e9493 100644 --- a/Tests/Functional/DataProcessing/TreeMenuProcessorTest.php +++ b/Tests/Functional/DataProcessing/TreeMenuProcessorTest.php @@ -14,12 +14,14 @@ use B13\Menus\DataProcessing\TreeMenu; use TYPO3\CMS\Core\Cache\CacheDataCollector; +use TYPO3\CMS\Core\Cache\CacheTag; use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder; use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageInformation; class TreeMenuProcessorTest extends DataProcessing { @@ -406,13 +408,17 @@ public function processTest(array $tsfe, array $configuration, array $expected): if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { $cacheDataCollector = new CacheDataCollector(); $request = $request->withAttribute('frontend.cache.collector', $cacheDataCollector); + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($tsfe['rootLine']); + $pageInformation->setId($tsfe['id']); + $request = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); + $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; + $GLOBALS['TSFE']->id = $tsfe['id']; } $GLOBALS['TYPO3_REQUEST'] = $request; - $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); - $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; - $GLOBALS['TSFE']->id = $tsfe['id']; - $treeMenuProccessor = GeneralUtility::makeInstance(TreeMenu::class); $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); $treeMenu = $treeMenuProccessor->process($contentObjectRenderer, [], $configuration, []); @@ -475,18 +481,28 @@ public function menuIdTagsAreAddedToPageCache(array $tsfe, int $entryPoints, arr if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { $cacheDataCollector = new CacheDataCollector(); $request = $request->withAttribute('frontend.cache.collector', $cacheDataCollector); + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($tsfe['rootLine']); + $pageInformation->setId($tsfe['id']); + $request = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); + $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; + $GLOBALS['TSFE']->id = $tsfe['id']; } $GLOBALS['TYPO3_REQUEST'] = $request; - $GLOBALS['TSFE'] = $this->getTypoScriptFrontendController($site, $tsfe['id']); - $GLOBALS['TSFE']->rootLine = $tsfe['rootLine']; - $GLOBALS['TSFE']->id = $tsfe['id']; $configuration = ['as' => 'my-tree', 'entryPoints' => $entryPoints, 'depth' => 2]; $treeMenuProccessor = GeneralUtility::makeInstance(TreeMenu::class); $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); $treeMenuProccessor->process($contentObjectRenderer, [], $configuration, []); - $pageCacheTags = $GLOBALS['TSFE']->getPageCacheTags(); + if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { + $pageCacheTags = array_map(fn (CacheTag $cacheTag) => $cacheTag->name, $cacheDataCollector->getCacheTags()); + } else { + $pageCacheTags = $GLOBALS['TSFE']->getPageCacheTags(); + } + self::assertSame(count($expectedTags), count($pageCacheTags)); foreach ($expectedTags as $expectedTag) { self::assertTrue(in_array($expectedTag, $pageCacheTags, true)); diff --git a/Tests/Unit/DataProcessing/BreadcrumbsMenuTest.php b/Tests/Unit/DataProcessing/BreadcrumbsMenuTest.php index 0b3ec7b..41b3a00 100644 --- a/Tests/Unit/DataProcessing/BreadcrumbsMenuTest.php +++ b/Tests/Unit/DataProcessing/BreadcrumbsMenuTest.php @@ -14,8 +14,12 @@ use B13\Menus\DataProcessing\BreadcrumbsMenu; use B13\Menus\Domain\Repository\MenuRepository; +use TYPO3\CMS\Core\Http\ServerRequest; +use TYPO3\CMS\Core\Information\Typo3Version; +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageInformation; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; class BreadcrumbsMenuTest extends UnitTestCase @@ -30,16 +34,25 @@ public function processMarksPageStates(): void ['uid' => 2], ]; - $GLOBALS['TSFE'] = new \stdClass(); - $GLOBALS['TSFE']->rootLine = [ + $rootLine = [ ['uid' => 1], ['uid' => 2], ]; - $GLOBALS['TSFE']->id = 2; + if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($rootLine); + $pageInformation->setId(2); + $request = GeneralUtility::makeInstance(ServerRequest::class); + $GLOBALS['TYPO3_REQUEST'] = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = new \stdClass(); + $GLOBALS['TSFE']->rootLine = $rootLine; + $GLOBALS['TSFE']->id = 2; + } $menuRepository = $this->getMockBuilder(MenuRepository::class) ->disableOriginalConstructor() ->getMock(); - $menuRepository->expects(self::once())->method('getBreadcrumbsMenu')->with($GLOBALS['TSFE']->rootLine, [])->willReturn($pages); + $menuRepository->expects(self::once())->method('getBreadcrumbsMenu')->with($rootLine, [])->willReturn($pages); $contentDataProcessor = $this->getMockBuilder(ContentDataProcessor::class) ->disableOriginalConstructor() ->getMock(); @@ -48,9 +61,10 @@ public function processMarksPageStates(): void ->getMock(); $contentObjectRenderer->expects(self::once())->method('stdWrapValue')->with('as', [], 'breadcrumbs')->willReturn('breadcrumbs'); $breadcrumbsMenuDataProcessor = $this->getMockBuilder(BreadcrumbsMenu::class) - ->onlyMethods(['processAdditionalDataProcessors']) + ->onlyMethods(['processAdditionalDataProcessors', 'getRootline']) ->setConstructorArgs([$contentDataProcessor, $menuRepository]) ->getMock(); + $breadcrumbsMenuDataProcessor->expects(self::once())->method('getRootline')->willReturn($rootLine); $processedData = $breadcrumbsMenuDataProcessor->process($contentObjectRenderer, [], [], []); self::assertTrue($processedData['breadcrumbs'][0]['isInRootLine']); self::assertTrue($processedData['breadcrumbs'][1]['isInRootLine']); diff --git a/Tests/Unit/Domain/Repository/MenuRepositoryTest.php b/Tests/Unit/Domain/Repository/MenuRepositoryTest.php index ad2bff6..a159125 100644 --- a/Tests/Unit/Domain/Repository/MenuRepositoryTest.php +++ b/Tests/Unit/Domain/Repository/MenuRepositoryTest.php @@ -18,6 +18,8 @@ use TYPO3\CMS\Core\Context\LanguageAspect; use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\Information\Typo3Version; +use TYPO3\CMS\Core\LinkHandling\PageTypeLinkResolver; +use TYPO3\CMS\Core\Schema\TcaSchemaFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -108,6 +110,10 @@ public function getPage($uid, $disableGroupAccessCheck = false) }; } else { $pageRepository = new class() extends PageRepository { + public function __construct(?Context $context = null, ?TcaSchemaFactory $tcaSchemaFactory = null, ?PageTypeLinkResolver $pageTypeLinkResolver = null) + { + } + public function getPage(int $uid, bool $disableGroupAccessCheck = false): array { if ($uid === 1) { @@ -120,9 +126,6 @@ public function getPage(int $uid, bool $disableGroupAccessCheck = false): array } return []; } - protected function init(): void - { - } }; } diff --git a/Tests/Unit/PageStateMarkerTest.php b/Tests/Unit/PageStateMarkerTest.php index fc3ed41..8ffa2ad 100644 --- a/Tests/Unit/PageStateMarkerTest.php +++ b/Tests/Unit/PageStateMarkerTest.php @@ -13,6 +13,10 @@ */ use B13\Menus\PageStateMarker; +use TYPO3\CMS\Core\Http\ServerRequest; +use TYPO3\CMS\Core\Information\Typo3Version; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Frontend\Page\PageInformation; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; class PageStateMarkerTest extends UnitTestCase @@ -28,12 +32,21 @@ public function markStatesRecursiveMarksLevel2Pages(): void ['uid' => 2], ], ]; - $GLOBALS['TSFE'] = new \stdClass(); - $GLOBALS['TSFE']->rootLine = [ + $rootLine = [ ['uid' => 1], ['uid' => 2], ]; - $GLOBALS['TSFE']->id = 2; + if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($rootLine); + $pageInformation->setId(2); + $request = GeneralUtility::makeInstance(ServerRequest::class); + $GLOBALS['TYPO3_REQUEST'] = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = new \stdClass(); + $GLOBALS['TSFE']->rootLine = $rootLine; + $GLOBALS['TSFE']->id = 2; + } PageStateMarker::markStatesRecursively($page, 1); self::assertTrue($page['isInRootLine']); self::assertTrue($page['subpages'][0]['isInRootLine']); @@ -57,13 +70,23 @@ public function markStatesRecursiveMarksLevel3Pages(): void ], ], ]; - $GLOBALS['TSFE'] = new \stdClass(); - $GLOBALS['TSFE']->rootLine = [ + + $rootLine = [ ['uid' => 1], ['uid' => 2], ['uid' => 3], ]; - $GLOBALS['TSFE']->id = 3; + if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() > 12) { + $pageInformation = new PageInformation(); + $pageInformation->setRootLine($rootLine); + $pageInformation->setId(3); + $request = GeneralUtility::makeInstance(ServerRequest::class); + $GLOBALS['TYPO3_REQUEST'] = $request->withAttribute('frontend.page.information', $pageInformation); + } else { + $GLOBALS['TSFE'] = new \stdClass(); + $GLOBALS['TSFE']->rootLine = $rootLine; + $GLOBALS['TSFE']->id = 3; + } PageStateMarker::markStatesRecursively($page, 1); self::assertTrue($page['isInRootLine']); self::assertTrue($page['subpages'][0]['isInRootLine']); diff --git a/ext_emconf.php b/ext_emconf.php index d0b2318..8d79e7c 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -11,7 +11,7 @@ 'author_company' => 'b13 GmbH', 'constraints' => [ 'depends' => [ - 'typo3' => '12.4.0-14.4.99', + 'typo3' => '11.5.0-14.4.99', ], ], ];