From 8333d23d65fd08590f0dc81de46f34df226b6c2a Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sun, 22 Feb 2026 12:30:36 +0100 Subject: [PATCH 1/4] [TASK] Upgrade PHPStan ecosystem to 2.x - phpstan/phpstan: ^1.12 -> ^2.1 - phpstan/phpstan-strict-rules: ^1.6 -> ^2.0 - symplify/phpstan-rules: ^13.0 -> ^14.9 - Removed deprecated symplify regex rules (dropped in 14.x) - Updated strictRules config: strictCalls -> strictFunctionCalls - Regenerated phpstan-baseline.neon for stricter 2.x rules --- composer.json | 6 +- composer.lock | 104 +++++------ phpstan-baseline.neon | 419 ++++++++++++++++++++++++++++++++++++++++-- phpstan.neon | 5 +- 4 files changed, 461 insertions(+), 73 deletions(-) diff --git a/composer.json b/composer.json index e6a384ef1..ee2f8844f 100644 --- a/composer.json +++ b/composer.json @@ -32,12 +32,12 @@ "ergebnis/composer-normalize": "^2.50", "friendsofphp/php-cs-fixer": "^3.94", "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^1.12", - "phpstan/phpstan-strict-rules": "^1.6", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^10.5", "symfony/console": "^6.4", "symplify/monorepo-builder": "^11.2.0", - "symplify/phpstan-rules": "^13.0" + "symplify/phpstan-rules": "^14.9" }, "replace": { "symfony/polyfill-php80": "*", diff --git a/composer.lock b/composer.lock index caa88f7da..9d31b269c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a4c16f301d12c383128e62eacaf5ff33", + "content-hash": "a539658a6108a63f5757061bda1439ce", "packages": [ { "name": "brotkrueml/twig-codehighlight", @@ -1487,16 +1487,16 @@ }, { "name": "nette/utils", - "version": "v4.0.8", + "version": "v4.0.10", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" + "reference": "2778deb6aab136c8db4ed1f4d6e9f465ca2dbee3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "url": "https://api.github.com/repos/nette/utils/zipball/2778deb6aab136c8db4ed1f4d6e9f465ca2dbee3", + "reference": "2778deb6aab136c8db4ed1f4d6e9f465ca2dbee3", "shasum": "" }, "require": { @@ -1570,9 +1570,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.8" + "source": "https://github.com/nette/utils/tree/v4.0.10" }, - "time": "2025-08-06T21:43:34+00:00" + "time": "2025-12-01T17:30:42+00:00" }, { "name": "phpdocumentor/dev-server", @@ -5335,28 +5335,28 @@ }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", "php": "^7.2 || ^8.0" }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { @@ -5387,9 +5387,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "packages-dev": [ @@ -6797,20 +6797,15 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.23", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "29201e7a743a6ab36f91394eab51889a82631428" - }, + "version": "2.1.39", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/29201e7a743a6ab36f91394eab51889a82631428", - "reference": "29201e7a743a6ab36f91394eab51889a82631428", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -6851,32 +6846,31 @@ "type": "github" } ], - "time": "2025-03-23T14:57:32+00:00" + "time": "2026-02-11T14:48:56+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "1.6.2", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "b564ca479e7e735f750aaac4935af965572a7845" + "reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b564ca479e7e735f750aaac4935af965572a7845", - "reference": "b564ca479e7e735f750aaac4935af965572a7845", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f", + "reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.12.4" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.39" }, "require-dev": { - "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-deprecation-rules": "^1.1", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -6896,11 +6890,14 @@ "MIT" ], "description": "Extra strict and opinionated rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.6.2" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.10" }, - "time": "2025-01-19T13:02:24+00:00" + "time": "2026-02-11T14:17:32+00:00" }, { "name": "phpunit/php-code-coverage", @@ -8506,23 +8503,23 @@ }, { "name": "symplify/phpstan-rules", - "version": "13.0.1", + "version": "14.9.11", "source": { "type": "git", "url": "https://github.com/symplify/phpstan-rules.git", - "reference": "c117396f4d7fe30704233c033244114e0fbea3f0" + "reference": "5ea4bbd9357cba253aada506dd96d37d7069ac3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/phpstan-rules/zipball/c117396f4d7fe30704233c033244114e0fbea3f0", - "reference": "c117396f4d7fe30704233c033244114e0fbea3f0", + "url": "https://api.github.com/repos/symplify/phpstan-rules/zipball/5ea4bbd9357cba253aada506dd96d37d7069ac3b", + "reference": "5ea4bbd9357cba253aada506dd96d37d7069ac3b", "shasum": "" }, "require": { - "nette/utils": "^3.2.9 || ^4.0", - "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.10.30", - "webmozart/assert": "^1.11" + "nette/utils": "^3.2|^4.0", + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.33", + "webmozart/assert": "^1.12 || ^2.0" }, "type": "phpstan-extension", "extra": { @@ -8533,6 +8530,9 @@ } }, "autoload": { + "files": [ + "src/functions/fast-functions.php" + ], "psr-4": { "Symplify\\PHPStanRules\\": "src" } @@ -8544,7 +8544,7 @@ "description": "Set of Symplify rules for PHPStan", "support": { "issues": "https://github.com/symplify/phpstan-rules/issues", - "source": "https://github.com/symplify/phpstan-rules/tree/13.0.1" + "source": "https://github.com/symplify/phpstan-rules/tree/14.9.11" }, "funding": [ { @@ -8556,7 +8556,7 @@ "type": "github" } ], - "time": "2024-08-23T09:02:23+00:00" + "time": "2026-01-05T13:53:59+00:00" }, { "name": "theseer/tokenizer", @@ -8624,5 +8624,5 @@ "platform-overrides": { "php": "8.1.27" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3af0ed08c..d3ae94a7c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,66 +1,457 @@ parameters: ignoreErrors: - - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" + message: '#^Method T3Docs\\Typo3DocsTheme\\Api\\Typo3ApiService\:\:decodeJson\(\) should return array\\>\|null but returns array\\.$#' + identifier: return.type + count: 1 + path: packages/typo3-docs-theme/src/Api/Typo3ApiService.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\AttachFileObjectsToFileTextRoleTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\CollectFileObjectsTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php + + - + message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' + identifier: foreach.nonIterable + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php + + - + message: '#^Binary operation "\." between mixed and string results in an error\.$#' + identifier: binaryOp.invalid count: 2 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\CollectPrefixLinkTargetsTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php + + - + message: '#^Parameter \#1 \$rawAnchor of method phpDocumentor\\Guides\\ReferenceResolvers\\AnchorNormalizer\:\:reduceAnchor\(\) expects string, mixed given\.$#' + identifier: argument.type + count: 2 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php + + - + message: '#^Parameter \#3 \$title of class phpDocumentor\\Guides\\Meta\\InternalTarget constructor expects string\|null, mixed given\.$#' + identifier: argument.type + count: 2 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\ConfvalMenuNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\RedirectsNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\ReplacePermalinksNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\Typo3TalkNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php + + - + message: '#^Parameter \#1 \$configs of method T3Docs\\Typo3DocsTheme\\DependencyInjection\\Typo3DocsThemeExtension\:\:getConfigValue\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 15 + path: packages/typo3-docs-theme/src/DependencyInjection/Typo3DocsThemeExtension.php + + - + message: '#^Parameter \#1 \$value of class phpDocumentor\\Guides\\Nodes\\CollectionNode constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/FigureDirective.php + + - + message: '#^Parameter \#5 \$value of class T3Docs\\Typo3DocsTheme\\Nodes\\GroupTabNode constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/GroupTabDirective.php + + - + message: '#^Parameter \#1 \$value of class phpDocumentor\\Guides\\Nodes\\CollectionNode constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/IncludeDirective.php + + - + message: '#^Cannot access property \$textContent on mixed\.$#' + identifier: property.nonObject + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#1 \$categories of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildCategoryConfvals\(\) expects array\\>, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#2 \$categoryArray of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:assignConfvalsToCategories\(\) expects array\\>, array\\> given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#2 \$settings of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\\>, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#3 \$categories of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\\>, mixed given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#3 \$subject of function preg_replace expects array\\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#4 \$labels of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#5 \$descriptions of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#6 \$categoryLabels of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#7 \$categoryArray of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfval\(\) expects array\\>, array\\> given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Parameter \#7 \$value of class phpDocumentor\\Guides\\RestructuredText\\Nodes\\ConfvalNode constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Possibly invalid array key type mixed\.$#' + identifier: offsetAccess.invalidOffset + count: 3 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Using nullsafe property access "\?\-\>textContent" on left side of \?\? is unnecessary\. Use \-\> instead\.$#' + identifier: nullsafe.neverNull + count: 1 + path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php + + - + message: '#^Method T3Docs\\Typo3DocsTheme\\Directives\\Typo3FileDirective\:\:processSub\(\) never returns null so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php + + - + message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' + identifier: foreach.nonIterable + count: 3 path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - message: "#^Cannot call method getValue\\(\\) on phpDocumentor\\\\Guides\\\\Nodes\\\\Node\\|null\\.$#" + message: '#^Cannot call method getValue\(\) on phpDocumentor\\Guides\\Nodes\\Node\|null\.$#' + identifier: method.nonObject count: 2 path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - message: "#^Parameter \\$description of class T3Docs\\\\Typo3DocsTheme\\\\Nodes\\\\ViewHelperNode constructor expects array\\, array\\, mixed\\> given\\.$#" + message: '#^Parameter \#1 \$argumentDefinition of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getArgument\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php + + - + message: '#^Parameter \#1 \$array of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getString\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php + + - + message: '#^Parameter \#1 \$value of class phpDocumentor\\Guides\\Nodes\\CollectionNode constructor expects list\, array\ given\.$#' + identifier: argument.type count: 1 path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - message: "#^Parameter \\$docTags of class T3Docs\\\\Typo3DocsTheme\\\\Nodes\\\\ViewHelperNode constructor expects array\\, mixed given\\.$#" + message: '#^Parameter \#2 \$data of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getViewHelperNode\(\) expects array\, array\ given\.$#' + identifier: argument.type count: 1 path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - message: "#^Parameter \\$documentation of class T3Docs\\\\Typo3DocsTheme\\\\Nodes\\\\ViewHelperNode constructor expects array\\, mixed given\\.$#" + message: '#^Parameter \#3 \$sourceEdit of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getViewHelperNode\(\) expects array\, mixed given\.$#' + identifier: argument.type count: 1 path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - message: "#^Result of && is always false\\.$#" + message: '#^Parameter \$description of class T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode constructor expects array\, list\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php + + - + message: '#^Parameter \$docTags of class T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode constructor expects array\, mixed given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php + + - + message: '#^Parameter \$documentation of class T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode constructor expects array\, mixed given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php + + - + message: '#^Parameter \#2 \$haystack of function in_array expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php + + - + message: '#^Parameter \#2 \$json of method phpDocumentor\\Guides\\ReferenceResolvers\\Interlink\\DefaultInventoryLoader\:\:loadInventoryFromJson\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Inventory/Typo3InventoryRepository.php + + - + message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Nodes/ConfvalMenuNode.php + + - + message: '#^Parameter \#1 \$value of method phpDocumentor\\Guides\\Nodes\\CompoundNode\\:\:__construct\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListItemNode.php + + - + message: '#^Parameter \#1 \$value of method phpDocumentor\\Guides\\Nodes\\CompoundNode\\:\:__construct\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListNode.php + + - + message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Nodes/GlossaryNode.php + + - + message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Nodes/MainMenuJsonNode.php + + - + message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php + + - + message: '#^Property T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode\:\:\$arguments \(array\\) does not accept array\\.$#' + identifier: assign.propertyType + count: 1 + path: packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php + + - + message: '#^PHPDoc tag @var with type phpDocumentor\\Guides\\RenderContext\|null is not subtype of type phpDocumentor\\Guides\\RenderContext\.$#' + identifier: varTag.type + count: 2 + path: packages/typo3-docs-theme/src/Twig/TwigExtension.php + + - + message: '#^Binary operation "\." between mixed and ''/guides\.xml'' results in an error\.$#' + identifier: binaryOp.invalid count: 1 path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - message: "#^SimpleXMLElement does not accept string\\.$#" - count: 4 + message: '#^Result of && is always false\.$#' + identifier: booleanAnd.alwaysFalse + count: 1 path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - message: "#^Undefined variable\\: \\$guides$#" + message: '#^Undefined variable\: \$guides$#' + identifier: variable.undefined count: 2 path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - message: "#^Variable \\$guides in isset\\(\\) is never defined\\.$#" + message: '#^Variable \$guides in isset\(\) is never defined\.$#' + identifier: isset.variable count: 1 path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - message: "#^Method T3Docs\\\\GuidesCli\\\\Migration\\\\SettingsMigrator\\:\\:collectUnmigratedLegacySettings\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Binary operation "\." between mixed and ''/\-/issues'' results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: packages/typo3-guides-cli/src/Command/InitCommand.php + + - + message: '#^Binary operation "\." between mixed and ''/issues'' results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: packages/typo3-guides-cli/src/Command/InitCommand.php + + - + message: '#^Binary operation "\." between mixed and ''/settings…'' results in an error\.$#' + identifier: binaryOp.invalid + count: 2 + path: packages/typo3-guides-cli/src/Command/InitCommand.php + + - + message: '#^Method T3Docs\\GuidesCli\\Command\\InitCommand\:\:fetchComposerArray\(\) should return array\\|null but returns array\\.$#' + identifier: return.type + count: 1 + path: packages/typo3-guides-cli/src/Command/InitCommand.php + + - + message: '#^Parameter \#1 \$string of function strtolower expects string, mixed given\.$#' + identifier: argument.type + count: 2 + path: packages/typo3-guides-cli/src/Command/InitCommand.php + + - + message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-guides-cli/src/Command/InitCommand.php + + - + message: '#^Binary operation "\." between mixed and ''/Settings\.cfg'' results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php + + - + message: '#^Binary operation "\." between mixed and ''/guides\.xml'' results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php + + - + message: '#^Method T3Docs\\GuidesCli\\Migration\\SettingsMigrator\:\:collectUnmigratedLegacySettings\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: packages/typo3-guides-cli/src/Migration/SettingsMigrator.php + + - + message: '#^Parameter \#3 \$messages of class T3Docs\\GuidesCli\\Migration\\Dto\\MigrationResult constructor expects list\, array given\.$#' + identifier: argument.type count: 1 path: packages/typo3-guides-cli/src/Migration/SettingsMigrator.php - - message: "#^Cannot access offset 'packages' on mixed\\.$#" + message: '#^Method T3Docs\\GuidesCli\\Repository\\LegacySettingsRepository\:\:get\(\) should return array\\> but returns array\.$#' + identifier: return.type + count: 1 + path: packages/typo3-guides-cli/src/Repository/LegacySettingsRepository.php + + - + message: '#^Binary operation "\." between ''xxx'' and mixed results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: packages/typo3-guides-cli/src/XmlValidator.php + + - + message: '#^Binary operation "\." between ''\'' and mixed results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: packages/typo3-guides-extension/src/Command/RunDecorator.php + + - + message: '#^Binary operation "\." between mixed and ''/''\|''\\\\'' results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: packages/typo3-guides-extension/src/Command/RunDecorator.php + + - + message: '#^Parameter \#1 \$messages of method Symfony\\Component\\Console\\Output\\OutputInterface\:\:write\(\) expects iterable\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: packages/typo3-guides-extension/src/Command/RunDecorator.php + + - + message: '#^Cannot access offset ''packages'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: packages/typo3-version-handling/src/Packagist/PackagistService.php + + - + message: '#^Cannot access offset 0 on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: packages/typo3-version-handling/src/Packagist/PackagistService.php + + - + message: '#^Cannot access offset string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: packages/typo3-version-handling/src/Packagist/PackagistService.php + + - + message: '#^Parameter \#1 \$composerJsonArray of method T3Docs\\VersionHandling\\Packagist\\PackagistService\:\:getComposerInfoFromJson\(\) expects array\, array\ given\.$#' + identifier: argument.type count: 1 path: packages/typo3-version-handling/src/Packagist/PackagistService.php - - message: "#^Cannot access offset 0 on mixed\\.$#" + message: '#^Parameter \#3 \$value of function curl_setopt expects bool, int given\.$#' + identifier: argument.type count: 1 path: packages/typo3-version-handling/src/Packagist/PackagistService.php - - message: "#^Cannot access offset string on mixed\\.$#" + message: '#^Parameter \#3 \$value of function curl_setopt expects non\-empty\-string, string given\.$#' + identifier: argument.type count: 1 path: packages/typo3-version-handling/src/Packagist/PackagistService.php diff --git a/phpstan.neon b/phpstan.neon index 94775d0de..86f7c36ef 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,14 +1,11 @@ includes: - phpstan-baseline.neon -rules: - - Symplify\PHPStanRules\Rules\AnnotateRegexClassConstWithRegexLinkRule - - Symplify\PHPStanRules\Rules\RegexSuffixInRegexConstantRule parameters: phpVersion: 80100 level: max strictRules: allRules: false - strictCalls: true + strictFunctionCalls: true requireParentConstructorCall: true inferPrivatePropertyTypeFromConstructor: true treatPhpDocTypesAsCertain: false From 278e21bf51cd47b2c052b4a73581d671b64d6daa Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sun, 22 Feb 2026 13:42:37 +0100 Subject: [PATCH 2/4] [TASK] Reduce PHPStan baseline by fixing low-hanging fruit Reduce PHPStan baseline from 102 to 55 errors (46% reduction) across 31 files by applying targeted fixes: - Fix @param type on Typo3DocsThemeExtension::load() (15 errors) - Add array_values() wrappers where list<> expected (12 errors) - Add @phpstan-ignore return.unusedType on NodeTransformers (9 errors) - Cast $input->getArgument() to (string) in CLI commands (7 errors) - Fix dead code bug: missing $guides in operateOnXmlGuides() (4 errors) - Remove unnecessary @var in TwigExtension (2 errors) - Add @return list to collectUnmigratedLegacySettings() (1 error) - Cast $errno to (string) in XmlValidator (1 error) - Fix nullsafe ?->textContent to -> in SiteSetSettingsDirective (1 error) - Cast $answer to (string) in InitCommand validators (3 errors) - Add type annotations for return.type entries (3 errors) - Type-hint RunDecorator process callback parameters (3 errors) - Cast $_SERVER['argv'] to array in AddThemeSettingsToProjectNode (1 error) --- .../src/Api/Typo3ApiService.php | 1 + ...chFileObjectsToFileTextRoleTransformer.php | 1 + .../CollectFileObjectsTransformer.php | 1 + .../CollectPrefixLinkTargetsTransformer.php | 1 + .../ConfvalMenuNodeTransformer.php | 1 + .../RedirectsNodeTransformer.php | 1 + ...encesFromCrossReferenceNodeTransformer.php | 1 + .../ReplacePermalinksNodeTransformer.php | 1 + .../Typo3TalkNodeTransformer.php | 1 + .../Typo3DocsThemeExtension.php | 2 +- .../src/Directives/FigureDirective.php | 2 +- .../src/Directives/GroupTabDirective.php | 2 +- .../src/Directives/IncludeDirective.php | 2 +- .../Directives/SiteSetSettingsDirective.php | 2 +- .../src/Directives/Typo3FileDirective.php | 1 + .../src/Directives/ViewHelperDirective.php | 2 +- .../AddThemeSettingsToProjectNode.php | 3 +- .../src/Nodes/ConfvalMenuNode.php | 2 +- .../DirectoryTreeListItemNode.php | 2 +- .../DirectoryTree/DirectoryTreeListNode.php | 2 +- .../src/Nodes/GlossaryNode.php | 2 +- .../src/Nodes/MainMenuJsonNode.php | 2 +- .../src/Nodes/ViewHelperNode.php | 2 +- .../src/Twig/TwigExtension.php | 6 +- .../src/Command/ConfigureCommand.php | 4 +- .../src/Command/InitCommand.php | 9 +- .../src/Command/MigrateSettingsCommand.php | 4 +- .../src/Migration/SettingsMigrator.php | 3 +- .../Repository/LegacySettingsRepository.php | 1 + .../typo3-guides-cli/src/XmlValidator.php | 2 +- .../src/Command/RunDecorator.php | 4 +- phpstan-baseline.neon | 246 ++---------------- 32 files changed, 61 insertions(+), 255 deletions(-) diff --git a/packages/typo3-docs-theme/src/Api/Typo3ApiService.php b/packages/typo3-docs-theme/src/Api/Typo3ApiService.php index 42768fb09..7ddd47486 100644 --- a/packages/typo3-docs-theme/src/Api/Typo3ApiService.php +++ b/packages/typo3-docs-theme/src/Api/Typo3ApiService.php @@ -135,6 +135,7 @@ private function decodeJson(string $jsonData): ?array return null; } + /** @var array> $apiData */ return $apiData; } diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php index 0f80d121d..fa822015d 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php @@ -64,6 +64,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php index 39eb6ba34..ac2cfd291 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php @@ -48,6 +48,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php index 61fa4d65d..f4639a4a6 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php @@ -104,6 +104,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if ($node instanceof DocumentNode) { diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php index df27d5302..6b13fba2f 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php @@ -35,6 +35,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { assert($node instanceof ConfvalMenuNode); diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php index 29756a775..bc1b85930 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php @@ -35,6 +35,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { assert($node instanceof CrossReferenceNode); diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php index 4bc8c8af6..3e37d8bcf 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php @@ -35,6 +35,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { assert($node instanceof CrossReferenceNode); diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php index cddf94557..acf2c2c66 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php @@ -32,6 +32,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { assert($node instanceof HyperLinkNode); diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php index 229b66daa..e6fcb8fc1 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php @@ -46,6 +46,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } + /** @phpstan-ignore return.unusedType */ public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if ($node instanceof DocumentNode) { diff --git a/packages/typo3-docs-theme/src/DependencyInjection/Typo3DocsThemeExtension.php b/packages/typo3-docs-theme/src/DependencyInjection/Typo3DocsThemeExtension.php index 05984ef7c..2cd1bf4c2 100644 --- a/packages/typo3-docs-theme/src/DependencyInjection/Typo3DocsThemeExtension.php +++ b/packages/typo3-docs-theme/src/DependencyInjection/Typo3DocsThemeExtension.php @@ -32,7 +32,7 @@ class Typo3DocsThemeExtension extends Extension implements PrependExtensionInter YoutubeNode::class => 'body/directive/youtube.html.twig', ]; - /** @param mixed[] $configs */ + /** @param array $configs */ public function load(array $configs, ContainerBuilder $container): void { $loader = new PhpFileLoader( diff --git a/packages/typo3-docs-theme/src/Directives/FigureDirective.php b/packages/typo3-docs-theme/src/Directives/FigureDirective.php index 3e2d45fba..c0e21bfd2 100644 --- a/packages/typo3-docs-theme/src/Directives/FigureDirective.php +++ b/packages/typo3-docs-theme/src/Directives/FigureDirective.php @@ -93,7 +93,7 @@ public function process( 'align' => $scalarOptions['align'] ?? null, ]); - $figureNode = new FigureNode($image, new CollectionNode($collectionNode->getChildren())); + $figureNode = new FigureNode($image, new CollectionNode(array_values($collectionNode->getChildren()))); // Build filtered options - copy all options but validate zoom mode // We must set all options ourselves because DirectiveRule::postProcessNode diff --git a/packages/typo3-docs-theme/src/Directives/GroupTabDirective.php b/packages/typo3-docs-theme/src/Directives/GroupTabDirective.php index 10b0cda80..7ab240986 100644 --- a/packages/typo3-docs-theme/src/Directives/GroupTabDirective.php +++ b/packages/typo3-docs-theme/src/Directives/GroupTabDirective.php @@ -47,7 +47,7 @@ protected function processSub( $directive->getData(), $directive->getDataNode() ?? new InlineCompoundNode(), $key, - $collectionNode->getChildren(), + array_values($collectionNode->getChildren()), ); } } diff --git a/packages/typo3-docs-theme/src/Directives/IncludeDirective.php b/packages/typo3-docs-theme/src/Directives/IncludeDirective.php index 84cb4a2ee..47cc5bb62 100644 --- a/packages/typo3-docs-theme/src/Directives/IncludeDirective.php +++ b/packages/typo3-docs-theme/src/Directives/IncludeDirective.php @@ -159,7 +159,7 @@ public function getCollectionFromPath(\League\Flysystem\FilesystemInterface|\php if ($directive->getOptionBool('show-buttons')) { $buttons[] = new EditOnGithubIncludeNode($path); } - return new CollectionNode(array_merge($buttons, $document->getChildren())); + return new CollectionNode(array_values(array_merge($buttons, $document->getChildren()))); } /** diff --git a/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php b/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php index 74038e342..08514d5be 100644 --- a/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php +++ b/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php @@ -102,7 +102,7 @@ public function processNode( if ($xml->loadXML($labelContents)) { foreach ($xml->getElementsByTagName('trans-unit') as $label) { $id = $label->getAttribute('id'); - $value = ($label->getElementsByTagName('source')[0] ?? null)?->textContent ?? ''; + $value = ($label->getElementsByTagName('source')[0] ?? null)->textContent ?? ''; if (!$value) { continue; } diff --git a/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php b/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php index c68ea4b0c..a9a56938a 100644 --- a/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php +++ b/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php @@ -43,6 +43,7 @@ public function getName(): string return self::NAME; } + /** @phpstan-ignore return.unusedType */ protected function processSub( BlockContext $blockContext, CollectionNode $collectionNode, diff --git a/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php b/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php index 59649eeb1..a59ff890f 100644 --- a/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php +++ b/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php @@ -160,7 +160,7 @@ private function getViewHelperNode(Directive $directive, array $data, array $sou $examples = []; if (str_contains($rawDocumentation, '```')) { $node = $this->markupLanguageParser->parse($blockContext->getDocumentParserContext()->getContext(), $rawDocumentation); - $collectionNode = new CollectionNode($node->getValue()); + $collectionNode = new CollectionNode(array_values($node->getValue())); foreach ($node->getValue() as $node) { if ($node instanceof ParagraphNode) { $description[] = $node; diff --git a/packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php b/packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php index e65e1a148..f1e4f7caf 100644 --- a/packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php +++ b/packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php @@ -21,7 +21,8 @@ public function __invoke(PostProjectNodeCreated $event): void // Native parsing of argv because we do not have the original ArgvInput // available, and neither the InputDefinition. That's ok for the // very basic parsing of a global option. - if (in_array('--minimal-test', $_SERVER['argv'] ?? [], true)) { + $argv = (array) ($_SERVER['argv'] ?? []); + if (in_array('--minimal-test', $argv, true)) { $settings = $event->getSettings(); // Set up input arguments for our minimal test. Will override diff --git a/packages/typo3-docs-theme/src/Nodes/ConfvalMenuNode.php b/packages/typo3-docs-theme/src/Nodes/ConfvalMenuNode.php index e9ae43c51..77d821029 100644 --- a/packages/typo3-docs-theme/src/Nodes/ConfvalMenuNode.php +++ b/packages/typo3-docs-theme/src/Nodes/ConfvalMenuNode.php @@ -35,7 +35,7 @@ public function __construct( private readonly bool $noindex = false, private readonly string $facet = 'Option', ) { - parent::__construct('confval-menu', $plainContent, $content, $value); + parent::__construct('confval-menu', $plainContent, $content, array_values($value)); } public function getId(): string diff --git a/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListItemNode.php b/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListItemNode.php index 911c041c9..b6fa1b4b7 100644 --- a/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListItemNode.php +++ b/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListItemNode.php @@ -18,7 +18,7 @@ public function __construct( private readonly string $name, private readonly array $subLists, ) { - parent::__construct($items); + parent::__construct(array_values($items)); } public function getName(): string diff --git a/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListNode.php b/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListNode.php index 5109a4af1..6f072b939 100644 --- a/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListNode.php +++ b/packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListNode.php @@ -12,7 +12,7 @@ final class DirectoryTreeListNode extends CompoundNode */ public function __construct(array $items, private readonly string $name) { - parent::__construct($items); + parent::__construct(array_values($items)); } public function getName(): string diff --git a/packages/typo3-docs-theme/src/Nodes/GlossaryNode.php b/packages/typo3-docs-theme/src/Nodes/GlossaryNode.php index 1bea24a8c..876535fe9 100644 --- a/packages/typo3-docs-theme/src/Nodes/GlossaryNode.php +++ b/packages/typo3-docs-theme/src/Nodes/GlossaryNode.php @@ -18,7 +18,7 @@ public function __construct( array $value = [], protected array $entries = [], ) { - parent::__construct('glossary', $plainContent, $content, $value); + parent::__construct('glossary', $plainContent, $content, array_values($value)); } /** diff --git a/packages/typo3-docs-theme/src/Nodes/MainMenuJsonNode.php b/packages/typo3-docs-theme/src/Nodes/MainMenuJsonNode.php index 03a2c3f4e..608388865 100644 --- a/packages/typo3-docs-theme/src/Nodes/MainMenuJsonNode.php +++ b/packages/typo3-docs-theme/src/Nodes/MainMenuJsonNode.php @@ -16,7 +16,7 @@ public function __construct( protected readonly InlineCompoundNode $content, array $value = [], ) { - parent::__construct('main-menu-json', $plainContent, $content, $value); + parent::__construct('main-menu-json', $plainContent, $content, array_values($value)); } } diff --git a/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php b/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php index 8e88b5f8b..4aaa6f1e9 100644 --- a/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php +++ b/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php @@ -41,7 +41,7 @@ public function __construct( private readonly array $display = [], private array $arguments = [], ) { - parent::__construct('viewhelper', $tagName, new InlineCompoundNode([new PlainTextInlineNode($tagName)]), $documentation); + parent::__construct('viewhelper', $tagName, new InlineCompoundNode([new PlainTextInlineNode($tagName)]), array_values($documentation)); } /** diff --git a/packages/typo3-docs-theme/src/Twig/TwigExtension.php b/packages/typo3-docs-theme/src/Twig/TwigExtension.php index dad53fd35..e3cc2635b 100644 --- a/packages/typo3-docs-theme/src/Twig/TwigExtension.php +++ b/packages/typo3-docs-theme/src/Twig/TwigExtension.php @@ -699,9 +699,8 @@ public function getPagerLinks(array $context): array */ public function getSingleHtmlLink(array $context): ?string { - /** @var RenderContext|null $renderContext */ $renderContext = $context['env'] ?? null; - if (!$renderContext) { + if (!$renderContext instanceof RenderContext) { return null; } @@ -729,9 +728,8 @@ public function getSingleHtmlLink(array $context): ?string */ public function getTopPageLink(array $context): ?PageLinkNode { - /** @var RenderContext|null $renderContext */ $renderContext = $context['env'] ?? null; - if (!$renderContext) { + if (!$renderContext instanceof RenderContext) { return null; } diff --git a/packages/typo3-guides-cli/src/Command/ConfigureCommand.php b/packages/typo3-guides-cli/src/Command/ConfigureCommand.php index dfd24edd5..d643ab940 100644 --- a/packages/typo3-guides-cli/src/Command/ConfigureCommand.php +++ b/packages/typo3-guides-cli/src/Command/ConfigureCommand.php @@ -255,7 +255,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln(print_r($input->getOptions(), true)); } - $config = $input->getArgument('input') . '/guides.xml'; + $config = (string) $input->getArgument('input') . '/guides.xml'; if ($output->isVeryVerbose()) { $output->writeln(sprintf('Config: %s', $config)); @@ -337,6 +337,8 @@ private function operateOnXmlProject(\SimpleXMLElement $xml, InputInterface $inp private function operateOnXmlGuides(\SimpleXMLElement $xml, InputInterface $input, OutputInterface $output): bool { + $guides = $xml->xpath('/ns:guides'); + $guidesVariables = [ 'input' => $input->getOption('guides-input'), 'input-file' => $input->getOption('guides-input-file'), diff --git a/packages/typo3-guides-cli/src/Command/InitCommand.php b/packages/typo3-guides-cli/src/Command/InitCommand.php index 01adc3729..203225ebb 100644 --- a/packages/typo3-guides-cli/src/Command/InitCommand.php +++ b/packages/typo3-guides-cli/src/Command/InitCommand.php @@ -98,7 +98,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $composerInfo->getComposerName() ); $projectNameQuestion->setValidator(function ($answer) { - if (is_null($answer) || trim($answer) === '') { + $answer = (string) $answer; + if (trim($answer) === '') { throw new \RuntimeException('The project title cannot be empty.'); } return $answer; @@ -124,7 +125,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'https://gitlab.com/' . $composerInfo->getComposerName(), ] ); - $repositoryUrl = $helper->ask($input, $output, $question); + $repositoryUrl = (string) $helper->ask($input, $output, $question); $question = $this->createValidatedUrlQuestion( 'Where can users report issues? [%s]', @@ -149,6 +150,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $question = new Question('Do you want generate some Documentation? (yes/no) ', 'yes'); $question->setValidator(function ($answer) { + $answer = (string) $answer; if (!in_array(strtolower($answer), [ 'yes', 'y', @@ -172,7 +174,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $siteSetDefinition = ''; if (is_string($siteSet) && $siteSet !== '') { $question = new Question('Enter the path to your site set: '); - $siteSetPath = $helper->ask($input, $output, $question); + $siteSetPath = (string) $helper->ask($input, $output, $question); if (is_file($siteSetPath . '/settings.definitions.yaml')) { $siteSetDefinition = $siteSetPath . '/settings.definitions.yaml'; } @@ -275,6 +277,7 @@ private function fetchComposerArray(): array|null return null; } + /** @var array $composerJson */ return $composerJson; } diff --git a/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php b/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php index ffd3a5ecf..ea98ee2c3 100644 --- a/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php +++ b/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php @@ -57,8 +57,8 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $settingsFile = $input->getArgument('input') . '/Settings.cfg'; - $guidesFile = $input->getArgument('input') . '/guides.xml'; + $settingsFile = (string) $input->getArgument('input') . '/Settings.cfg'; + $guidesFile = (string) $input->getArgument('input') . '/guides.xml'; if (file_exists($guidesFile) && !$input->getOption('force')) { $output->writeln('Target file already exists in specified directory (' . $guidesFile . ')'); diff --git a/packages/typo3-guides-cli/src/Migration/SettingsMigrator.php b/packages/typo3-guides-cli/src/Migration/SettingsMigrator.php index ce68c77d0..ba4de1fe5 100644 --- a/packages/typo3-guides-cli/src/Migration/SettingsMigrator.php +++ b/packages/typo3-guides-cli/src/Migration/SettingsMigrator.php @@ -56,7 +56,7 @@ public function migrate(array $legacySettings): MigrationResult // Attach the element to the root XML $this->xmlDocument->appendChild($guides); - return new MigrationResult($this->xmlDocument, $this->convertedSettings, $messages); + return new MigrationResult($this->xmlDocument, $this->convertedSettings, array_values($messages)); } private function createRootElement(): \DOMElement @@ -158,6 +158,7 @@ private function createInventorySection(): array return $inventories; } + /** @return list */ private function collectUnmigratedLegacySettings(): array { $messages = []; diff --git a/packages/typo3-guides-cli/src/Repository/LegacySettingsRepository.php b/packages/typo3-guides-cli/src/Repository/LegacySettingsRepository.php index 17380cc2b..d632ab3b7 100644 --- a/packages/typo3-guides-cli/src/Repository/LegacySettingsRepository.php +++ b/packages/typo3-guides-cli/src/Repository/LegacySettingsRepository.php @@ -37,6 +37,7 @@ public function get(string $filePath): array throw FileException::notParsable($filePath); } + /** @var array> $settings */ return $settings; } } diff --git a/packages/typo3-guides-cli/src/XmlValidator.php b/packages/typo3-guides-cli/src/XmlValidator.php index 3694233e1..2b2d99c3c 100644 --- a/packages/typo3-guides-cli/src/XmlValidator.php +++ b/packages/typo3-guides-cli/src/XmlValidator.php @@ -72,7 +72,7 @@ public function validate(): bool // Custom error handler function within the class private function errorHandler(mixed $errno, string $errstr): void { - $this->errors[] = 'xxx' . $errno . ': ' . $errstr; + $this->errors[] = 'xxx' . (string) $errno . ': ' . $errstr; } public function showErrors(OutputInterface $output): void diff --git a/packages/typo3-guides-extension/src/Command/RunDecorator.php b/packages/typo3-guides-extension/src/Command/RunDecorator.php index cf5d471da..d8c9c39e6 100644 --- a/packages/typo3-guides-extension/src/Command/RunDecorator.php +++ b/packages/typo3-guides-extension/src/Command/RunDecorator.php @@ -249,7 +249,7 @@ public function renderSingleLocalization(string $availableLocalization, array $b { $localInputDirectives = []; foreach ($baseInputDirectives as $baseInputDirectiveKey => $baseInputDirectiveValue) { - $localInputDirectives[$baseInputDirectiveKey] = $baseInputDirectiveValue . DIRECTORY_SEPARATOR . $availableLocalization; + $localInputDirectives[$baseInputDirectiveKey] = (string) $baseInputDirectiveValue . DIRECTORY_SEPARATOR . $availableLocalization; } $output->writeln(sprintf('Trying to render %s ...', $availableLocalization)); @@ -285,7 +285,7 @@ public function renderSingleLocalization(string $availableLocalization, array $b $process = new Process($processArguments); $output->writeln(sprintf('SUB-PROCESS: %s', $process->getCommandLine())); $hasErrors = false; - $result = $process->run(function ($type, $buffer) use ($output, &$hasErrors): void { + $result = $process->run(function (string $type, string $buffer) use ($output, &$hasErrors): void { if ($type === Process::ERR) { $output->write('' . $buffer . ''); $hasErrors = true; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d3ae94a7c..08756ffcf 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,23 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Api\\Typo3ApiService\:\:decodeJson\(\) should return array\\>\|null but returns array\\.$#' - identifier: return.type - count: 1 - path: packages/typo3-docs-theme/src/Api/Typo3ApiService.php - - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\AttachFileObjectsToFileTextRoleTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php - - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\CollectFileObjectsTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' identifier: foreach.nonIterable @@ -30,12 +12,6 @@ parameters: count: 2 path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\CollectPrefixLinkTargetsTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php - - message: '#^Parameter \#1 \$rawAnchor of method phpDocumentor\\Guides\\ReferenceResolvers\\AnchorNormalizer\:\:reduceAnchor\(\) expects string, mixed given\.$#' identifier: argument.type @@ -49,59 +25,11 @@ parameters: path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\ConfvalMenuNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php - - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\RedirectsNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php - - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php - - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\ReplacePermalinksNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php - - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Compiler\\NodeTransformers\\Typo3TalkNodeTransformer\:\:leaveNode\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php - - - - message: '#^Parameter \#1 \$configs of method T3Docs\\Typo3DocsTheme\\DependencyInjection\\Typo3DocsThemeExtension\:\:getConfigValue\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 15 + message: '#^Parameter \#1 \$configs \(array\\) of method T3Docs\\Typo3DocsTheme\\DependencyInjection\\Typo3DocsThemeExtension\:\:load\(\) should be contravariant with parameter \$configs \(array\\>\) of method Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface\:\:load\(\)$#' + identifier: method.childParameterType + count: 2 path: packages/typo3-docs-theme/src/DependencyInjection/Typo3DocsThemeExtension.php - - - message: '#^Parameter \#1 \$value of class phpDocumentor\\Guides\\Nodes\\CollectionNode constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/FigureDirective.php - - - - message: '#^Parameter \#5 \$value of class T3Docs\\Typo3DocsTheme\\Nodes\\GroupTabNode constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/GroupTabDirective.php - - - - message: '#^Parameter \#1 \$value of class phpDocumentor\\Guides\\Nodes\\CollectionNode constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/IncludeDirective.php - - message: '#^Cannot access property \$textContent on mixed\.$#' identifier: property.nonObject @@ -174,18 +102,6 @@ parameters: count: 3 path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - message: '#^Using nullsafe property access "\?\-\>textContent" on left side of \?\? is unnecessary\. Use \-\> instead\.$#' - identifier: nullsafe.neverNull - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Method T3Docs\\Typo3DocsTheme\\Directives\\Typo3FileDirective\:\:processSub\(\) never returns null so it can be removed from the return type\.$#' - identifier: return.unusedType - count: 1 - path: packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' identifier: foreach.nonIterable @@ -210,12 +126,6 @@ parameters: count: 1 path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - message: '#^Parameter \#1 \$value of class phpDocumentor\\Guides\\Nodes\\CollectionNode constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - message: '#^Parameter \#2 \$data of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getViewHelperNode\(\) expects array\, array\ given\.$#' identifier: argument.type @@ -246,54 +156,12 @@ parameters: count: 1 path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - message: '#^Parameter \#2 \$haystack of function in_array expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php - - message: '#^Parameter \#2 \$json of method phpDocumentor\\Guides\\ReferenceResolvers\\Interlink\\DefaultInventoryLoader\:\:loadInventoryFromJson\(\) expects array\, array\ given\.$#' identifier: argument.type count: 1 path: packages/typo3-docs-theme/src/Inventory/Typo3InventoryRepository.php - - - message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Nodes/ConfvalMenuNode.php - - - - message: '#^Parameter \#1 \$value of method phpDocumentor\\Guides\\Nodes\\CompoundNode\\:\:__construct\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListItemNode.php - - - - message: '#^Parameter \#1 \$value of method phpDocumentor\\Guides\\Nodes\\CompoundNode\\:\:__construct\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Nodes/DirectoryTree/DirectoryTreeListNode.php - - - - message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Nodes/GlossaryNode.php - - - - message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Nodes/MainMenuJsonNode.php - - - - message: '#^Parameter \#4 \$value of method phpDocumentor\\Guides\\RestructuredText\\Nodes\\GeneralDirectiveNode\:\:__construct\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php - - message: '#^Property T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode\:\:\$arguments \(array\\) does not accept array\\.$#' identifier: assign.propertyType @@ -301,122 +169,44 @@ parameters: path: packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php - - message: '#^PHPDoc tag @var with type phpDocumentor\\Guides\\RenderContext\|null is not subtype of type phpDocumentor\\Guides\\RenderContext\.$#' - identifier: varTag.type - count: 2 - path: packages/typo3-docs-theme/src/Twig/TwigExtension.php - - - - message: '#^Binary operation "\." between mixed and ''/guides\.xml'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - - - message: '#^Result of && is always false\.$#' - identifier: booleanAnd.alwaysFalse + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 1 path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - message: '#^Undefined variable\: \$guides$#' - identifier: variable.undefined - count: 2 - path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - - - message: '#^Variable \$guides in isset\(\) is never defined\.$#' - identifier: isset.variable - count: 1 - path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - - - message: '#^Binary operation "\." between mixed and ''/\-/issues'' results in an error\.$#' - identifier: binaryOp.invalid + message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType count: 1 path: packages/typo3-guides-cli/src/Command/InitCommand.php - - message: '#^Binary operation "\." between mixed and ''/issues'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string + count: 4 path: packages/typo3-guides-cli/src/Command/InitCommand.php - - message: '#^Binary operation "\." between mixed and ''/settings…'' results in an error\.$#' - identifier: binaryOp.invalid + message: '#^Variable \$repositoryUrl on left side of \?\? always exists and is not nullable\.$#' + identifier: nullCoalesce.variable count: 2 path: packages/typo3-guides-cli/src/Command/InitCommand.php - - message: '#^Method T3Docs\\GuidesCli\\Command\\InitCommand\:\:fetchComposerArray\(\) should return array\\|null but returns array\\.$#' - identifier: return.type - count: 1 - path: packages/typo3-guides-cli/src/Command/InitCommand.php - - - - message: '#^Parameter \#1 \$string of function strtolower expects string, mixed given\.$#' - identifier: argument.type + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 2 - path: packages/typo3-guides-cli/src/Command/InitCommand.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-guides-cli/src/Command/InitCommand.php - - - - message: '#^Binary operation "\." between mixed and ''/Settings\.cfg'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 path: packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php - - message: '#^Binary operation "\." between mixed and ''/guides\.xml'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php - - - - message: '#^Method T3Docs\\GuidesCli\\Migration\\SettingsMigrator\:\:collectUnmigratedLegacySettings\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: packages/typo3-guides-cli/src/Migration/SettingsMigrator.php - - - - message: '#^Parameter \#3 \$messages of class T3Docs\\GuidesCli\\Migration\\Dto\\MigrationResult constructor expects list\, array given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-guides-cli/src/Migration/SettingsMigrator.php - - - - message: '#^Method T3Docs\\GuidesCli\\Repository\\LegacySettingsRepository\:\:get\(\) should return array\\> but returns array\.$#' - identifier: return.type - count: 1 - path: packages/typo3-guides-cli/src/Repository/LegacySettingsRepository.php - - - - message: '#^Binary operation "\." between ''xxx'' and mixed results in an error\.$#' - identifier: binaryOp.invalid + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 1 path: packages/typo3-guides-cli/src/XmlValidator.php - - message: '#^Binary operation "\." between ''\'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: packages/typo3-guides-extension/src/Command/RunDecorator.php - - - - message: '#^Binary operation "\." between mixed and ''/''\|''\\\\'' results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: packages/typo3-guides-extension/src/Command/RunDecorator.php - - - - message: '#^Parameter \#1 \$messages of method Symfony\\Component\\Console\\Output\\OutputInterface\:\:write\(\) expects iterable\|string, mixed given\.$#' - identifier: argument.type + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 1 path: packages/typo3-guides-extension/src/Command/RunDecorator.php From bb62836111ab3a94e8d97515ac4f3bed807608e0 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sun, 22 Feb 2026 14:23:14 +0100 Subject: [PATCH 3/4] [TASK] Fix PHPStan errors with proper type handling instead of suppressions Replace inline @phpstan-ignore annotations with genuine code fixes: - Narrow return types from Node|null to Node (valid PHP covariance) - Add is_string()/is_array() guards for mixed Symfony Console inputs - Type-safe extraction of JSON/YAML parsed data with step-by-step validation - Fix array type annotations (array instead of array) - Properly handle DOMNodeList access in SiteSetSettingsDirective Baseline reduced from 41 to 9 entries (vs 13 on main). Remaining 9 are unfixable external library constraints (phpDocumentor interfaces returning mixed, Symfony ExtensionInterface contravariance). --- ...chFileObjectsToFileTextRoleTransformer.php | 3 +- .../CollectFileObjectsTransformer.php | 3 +- .../CollectPrefixLinkTargetsTransformer.php | 3 +- .../ConfvalMenuNodeTransformer.php | 3 +- .../RedirectsNodeTransformer.php | 3 +- ...encesFromCrossReferenceNodeTransformer.php | 3 +- .../ReplacePermalinksNodeTransformer.php | 3 +- .../Typo3TalkNodeTransformer.php | 3 +- .../Directives/SiteSetSettingsDirective.php | 68 +++--- .../src/Directives/Typo3FileDirective.php | 3 +- .../src/Directives/ViewHelperDirective.php | 79 +++++-- .../Inventory/Typo3InventoryRepository.php | 3 +- .../src/Nodes/ViewHelperNode.php | 2 +- .../src/Command/ConfigureCommand.php | 7 +- .../src/Command/InitCommand.php | 25 +- .../src/Command/MigrateSettingsCommand.php | 9 +- .../typo3-guides-cli/src/XmlValidator.php | 4 +- .../src/Command/RunDecorator.php | 5 +- .../src/Packagist/PackagistService.php | 20 +- phpstan-baseline.neon | 216 ------------------ 20 files changed, 159 insertions(+), 306 deletions(-) diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php index fa822015d..4b4b4f6f8 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/AttachFileObjectsToFileTextRoleTransformer.php @@ -64,8 +64,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { return $node; } diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php index ac2cfd291..0937f2d2b 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectFileObjectsTransformer.php @@ -48,8 +48,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { return $node; } diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php index f4639a4a6..33407d1f0 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/CollectPrefixLinkTargetsTransformer.php @@ -104,8 +104,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof DocumentNode) { $this->documentStack->pop(); diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php index 6b13fba2f..6d6fa9f62 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ConfvalMenuNodeTransformer.php @@ -35,8 +35,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { assert($node instanceof ConfvalMenuNode); if (count($node->getConfvals()) > 0) { diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php index bc1b85930..8f99f3e89 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RedirectsNodeTransformer.php @@ -35,8 +35,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { assert($node instanceof CrossReferenceNode); if ($node->getInterlinkDomain() === '') { diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php index 3e37d8bcf..145e490b5 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/RemoveInterlinkSelfReferencesFromCrossReferenceNodeTransformer.php @@ -35,8 +35,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { assert($node instanceof CrossReferenceNode); if (!$this->themeSettings->hasSettings('interlink_shortcode')) { diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php index acf2c2c66..4edb1c41c 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/ReplacePermalinksNodeTransformer.php @@ -32,8 +32,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { assert($node instanceof HyperLinkNode); if (!str_starts_with($node->getTargetReference(), 'https://docs.typo3.org/permalink/')) { diff --git a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php index e6fcb8fc1..88d0217ab 100644 --- a/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php +++ b/packages/typo3-docs-theme/src/Compiler/NodeTransformers/Typo3TalkNodeTransformer.php @@ -46,8 +46,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) return $node; } - /** @phpstan-ignore return.unusedType */ - public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof DocumentNode) { $this->sectionStack = []; diff --git a/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php b/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php index 08514d5be..cda78f18e 100644 --- a/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php +++ b/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php @@ -63,6 +63,10 @@ public function processNode( if (!is_array($yamlData) || !is_array($yamlData['settings'] ?? false)) { throw new FileLoadingException(sprintf('The .. typo3:site-set-settings:: source at path %s did not contain any settings ', $directive->getData())); } + /** @var array> $settings */ + $settings = $yamlData['settings']; + /** @var array> $yamlCategories */ + $yamlCategories = is_array($yamlData['categories'] ?? null) ? $yamlData['categories'] : []; } catch (FileLoadingException $exception) { $this->logger->warning($exception->getMessage(), $blockContext->getLoggerInformation()); return $this->getErrorNode(); @@ -85,9 +89,9 @@ public function processNode( $labelContents = ''; // Assume all EXT: references are relative to the rendered PROJECT - $labelsFile = $labelsFile ? - preg_replace('/^EXT:[^\/]*\//', 'PROJECT:/', $labelsFile) : - dirname($setConfigurationFile) . '/labels.xlf'; + $labelsFile = is_string($labelsFile) && $labelsFile !== '' + ? (preg_replace('/^EXT:[^\/]*\//', 'PROJECT:/', $labelsFile) ?? $labelsFile) + : dirname($setConfigurationFile) . '/labels.xlf'; try { $labelContents = $this->loadFileFromDocumentation($blockContext, $labelsFile); } catch (FileLoadingException $exception) { @@ -102,7 +106,8 @@ public function processNode( if ($xml->loadXML($labelContents)) { foreach ($xml->getElementsByTagName('trans-unit') as $label) { $id = $label->getAttribute('id'); - $value = ($label->getElementsByTagName('source')[0] ?? null)->textContent ?? ''; + $sourceElements = $label->getElementsByTagName('source'); + $value = $sourceElements->length > 0 ? ($sourceElements->item(0)->textContent ?? '') : ''; if (!$value) { continue; } @@ -117,7 +122,7 @@ public function processNode( } } - return $this->buildConfvalMenu($directive, $yamlData['settings'], $yamlData['categories'] ?? [], $labels, $descriptions, $categoryLabels); + return $this->buildConfvalMenu($directive, $settings, $yamlCategories, $labels, $descriptions, $categoryLabels); } /** @@ -174,7 +179,7 @@ private function getErrorNode(): ParagraphNode } /** - * @param array> $settings + * @param array> $settings * @param array> $categories * @param array $labels * @param array $descriptions @@ -187,18 +192,24 @@ public function buildConfvalMenu(Directive $directive, array $settings, array $c $idPrefix = $directive->getOptionString('name') . '-'; } $categoryArray = $this->buildCategoryArray($categories, $categoryLabels); + /** @var list> $rootCategories */ $rootCategories = []; foreach ($categoryArray as $key => $category) { - if (isset($categoryArray[$category['parent']])) { - assert(is_array($categoryArray[$category['parent']]['children'])); - $categoryArray[$category['parent']]['children'][] = &$categoryArray[$key]; + $parent = is_string($category['parent'] ?? null) ? $category['parent'] : ''; + if ($parent !== '' && isset($categoryArray[$parent])) { + assert(is_array($categoryArray[$parent]['children'])); + $categoryArray[$parent]['children'][] = &$categoryArray[$key]; } else { $rootCategories[] = &$categoryArray[$key]; } } foreach ($settings as $key => $setting) { - $confval = $this->buildConfval($setting, $idPrefix, $key, $directive, $labels, $descriptions, $categoryArray); - $this->assignConfvalsToCategories($setting['category'] ?? '', $categoryArray, $confval, $rootCategories); + if (!is_array($setting)) { + continue; + } + $confval = $this->buildConfval($setting, $idPrefix, (string) $key, $directive, $labels, $descriptions, $categoryArray); + $settingCategory = is_string($setting['category'] ?? null) ? $setting['category'] : ''; + $this->assignConfvalsToCategories($settingCategory, $categoryArray, $confval, $rootCategories); } $confvals = $this->buildCategoryConfvals($rootCategories, $idPrefix, $directive); $reservedParameterNames = [ @@ -237,13 +248,14 @@ public function buildConfvalMenu(Directive $directive, array $settings, array $c /** - * @param array> $setting + * @param array $setting * @param array $labels * @param array $descriptions - * @param array> $categoryArray + * @param array> $categoryArray */ public function buildConfval(array $setting, string $idPrefix, string $key, Directive $directive, array $labels, array $descriptions, array $categoryArray): ConfvalNode { + /** @var list $content */ $content = []; $description = $setting['description'] ?? $descriptions[$key] ?? false; if (is_string($description)) { @@ -315,12 +327,13 @@ private function customPrint(mixed $value): string /** - * @param array> $categoryArray + * @param array> $categoryArray */ private function getCategoryRootline(array $categoryArray, string $key): string { - $label = $categoryArray[$key]['label'] ?? $key; - $parent = $categoryArray[$key]['parent'] ?? ''; + $categoryEntry = $categoryArray[$key] ?? []; + $label = is_string($categoryEntry['label'] ?? null) ? $categoryEntry['label'] : $key; + $parent = is_string($categoryEntry['parent'] ?? null) ? $categoryEntry['parent'] : ''; if ($parent === '') { return $label; } @@ -331,7 +344,7 @@ private function getCategoryRootline(array $categoryArray, string $key): string } /** - * @param array> $categories + * @param array> $categories * @param array $categoryLabels * @return array> */ @@ -352,7 +365,7 @@ public function buildCategoryArray(array $categories, array $categoryLabels): ar /** * @param array> $categoryArray - * @param array> $rootCategories + * @param list> $rootCategories */ public function assignConfvalsToCategories(string $category, array &$categoryArray, ConfvalNode $confval, array &$rootCategories): void { @@ -371,7 +384,7 @@ public function assignConfvalsToCategories(string $category, array &$categoryArr } /** - * @param array> $categories + * @param list> $categories * @return ConfvalNode[] */ private function buildCategoryConfvals(array $categories, string $idPrefix, Directive $directive): array @@ -382,23 +395,24 @@ private function buildCategoryConfvals(array $categories, string $idPrefix, Dire $confvals = []; foreach ($categories as $category) { $children = []; - if (is_array($category['children'] ?? false)) { - $children = $this->buildCategoryConfvals($category['children'], $idPrefix, $directive); + $categoryChildren = $category['children'] ?? []; + if (is_array($categoryChildren) && $categoryChildren !== []) { + /** @var list> $categoryChildren */ + $children = $this->buildCategoryConfvals($categoryChildren, $idPrefix, $directive); } - $key = $category['key']; + $key = is_string($category['key'] ?? null) ? $category['key'] : ''; if ($key === '') { $key = '_global'; } - assert(is_string($key)); $additionalFields = []; $additionalFields['searchFacet'] = new InlineCompoundNode([new PlainTextInlineNode(self::CATEGORY_FACET)]); - $label = $category['label']; + $label = is_string($category['label'] ?? null) ? $category['label'] : ''; if ($label !== '') { - assert(is_string($label)); $additionalFields['Label'] = new InlineCompoundNode([new PlainTextInlineNode($label)]); } - assert(is_array($category['confvals'])); + /** @var list $categoryConfvals */ + $categoryConfvals = is_array($category['confvals'] ?? null) ? $category['confvals'] : []; $confvals[] = new ConfvalNode( $this->anchorNormalizer->reduceAnchor($idPrefix . 'category-' . $key), $key, @@ -406,7 +420,7 @@ private function buildCategoryConfvals(array $categories, string $idPrefix, Dire false, null, $additionalFields, - array_merge($children, $category['confvals']), + array_values(array_merge($children, $categoryConfvals)), $directive->getOptionBool('noindex'), ); } diff --git a/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php b/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php index a9a56938a..cebd08ae0 100644 --- a/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php +++ b/packages/typo3-docs-theme/src/Directives/Typo3FileDirective.php @@ -43,12 +43,11 @@ public function getName(): string return self::NAME; } - /** @phpstan-ignore return.unusedType */ protected function processSub( BlockContext $blockContext, CollectionNode $collectionNode, Directive $directive, - ): Node|null { + ): Node { $filename = $directive->getData(); $path = $directive->getOptionString('path'); $language = $directive->getOptionString('language'); diff --git a/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php b/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php index a59ff890f..71a14fa17 100644 --- a/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php +++ b/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php @@ -95,12 +95,20 @@ public function processNode( $noindex = $directive->getOptionBool('noindex'); + /** @var array $data */ $data = $json['viewHelpers'][$directive->getData()]; - $viewHelperNode = $this->getViewHelperNode($directive, $data, $json['sourceEdit'] ?? [], $blockContext, $noindex); + /** @var array $sourceEdit */ + $sourceEdit = is_array($json['sourceEdit'] ?? null) ? $json['sourceEdit'] : []; + $viewHelperNode = $this->getViewHelperNode($directive, $data, $sourceEdit, $blockContext, $noindex); + /** @var array $arguments */ $arguments = []; - foreach ($json['viewHelpers'][$directive->getData()]['argumentDefinitions'] ?? [] as $argumentDefinition) { - if (is_array($argumentDefinition)) { - $arguments[$this->getString($argumentDefinition, 'name')] = $this->getArgument($argumentDefinition, $viewHelperNode, $noindex); + $argumentDefs = $data['argumentDefinitions'] ?? []; + if (is_array($argumentDefs)) { + foreach ($argumentDefs as $argumentDefinition) { + if (is_array($argumentDefinition)) { + /** @var array $argumentDefinition */ + $arguments[$this->getString($argumentDefinition, 'name')] = $this->getArgument($argumentDefinition, $viewHelperNode, $noindex); + } } } if ($sortBy === 'name') { @@ -155,35 +163,43 @@ public function getArgument(array $argumentDefinition, ViewHelperNode $viewHelpe private function getViewHelperNode(Directive $directive, array $data, array $sourceEdit, BlockContext $blockContext, bool $noindex): ViewHelperNode { $rawDocumentation = $this->getString($data, 'documentation'); + /** @var list $description */ $description = []; + /** @var list $sections */ $sections = []; + /** @var list $examples */ $examples = []; if (str_contains($rawDocumentation, '```')) { - $node = $this->markupLanguageParser->parse($blockContext->getDocumentParserContext()->getContext(), $rawDocumentation); - $collectionNode = new CollectionNode(array_values($node->getValue())); - foreach ($node->getValue() as $node) { - if ($node instanceof ParagraphNode) { - $description[] = $node; + $documentNode = $this->markupLanguageParser->parse($blockContext->getDocumentParserContext()->getContext(), $rawDocumentation); + /** @var list $childNodes */ + $childNodes = array_values($documentNode->getChildren()); + $collectionNode = new CollectionNode($childNodes); + foreach ($childNodes as $childNode) { + if ($childNode instanceof ParagraphNode) { + $description[] = $childNode; } - if ($node instanceof CodeNode) { - $examples[] = $node; + if ($childNode instanceof CodeNode) { + $examples[] = $childNode; } } } else { $rstContentBlockContext = new BlockContext($blockContext->getDocumentParserContext(), $rawDocumentation, false); - $collectionNode = $this->startingRule->apply($rstContentBlockContext); - foreach ($collectionNode->getValue() as $node) { - if (!$node instanceof SectionNode) { - $description[] = $node; + $parsedNode = $this->startingRule->apply($rstContentBlockContext); + $collectionNode = $parsedNode instanceof CollectionNode ? $parsedNode : new CollectionNode([]); + /** @var list $collectionChildren */ + $collectionChildren = $collectionNode->getChildren(); + foreach ($collectionChildren as $childNode) { + if (!$childNode instanceof SectionNode) { + $description[] = $childNode; } } - foreach ($collectionNode->getValue() as $node) { - if ($node instanceof SectionNode) { - $title = $node->getTitle()->toString(); + foreach ($collectionChildren as $childNode) { + if ($childNode instanceof SectionNode) { + $title = $childNode->getTitle()->toString(); if (stripos($title, 'example') !== false) { // Case-insensitive check for 'example' - $examples[] = $node; + $examples[] = $childNode; } else { - $sections[] = $node; + $sections[] = $childNode; } } } @@ -207,13 +223,13 @@ private function getViewHelperNode(Directive $directive, array $data, array $sou shortClassName: $shortClassName, namespace: $nameSpace, className: $className, - documentation: $collectionNode?->getValue() ?? [], + documentation: $collectionNode->getChildren(), description: $description, sections: $sections, examples: $examples, xmlNamespace: $xmlNamespace, allowsArbitraryArguments: ($data['allowsArbitraryArguments'] ?? false) === true, - docTags: $data['docTags'] ?? [], + docTags: $this->extractDocTags($data), gitHubLink: $gitHubLink, noindex: $noindex, display: $display, @@ -222,6 +238,25 @@ className: $className, return $viewHelperNode; } + /** + * @param array $data + * @return array + */ + private function extractDocTags(array $data): array + { + $docTags = $data['docTags'] ?? []; + if (!is_array($docTags)) { + return []; + } + $result = []; + foreach ($docTags as $key => $value) { + if (is_string($key) && is_scalar($value)) { + $result[$key] = (string) $value; + } + } + return $result; + } + private function getErrorNode(): ParagraphNode { return new ParagraphNode([new InlineCompoundNode([new PlainTextInlineNode('The ViewHelper cannot be displayed.')])]); diff --git a/packages/typo3-docs-theme/src/Inventory/Typo3InventoryRepository.php b/packages/typo3-docs-theme/src/Inventory/Typo3InventoryRepository.php index 81cc92494..96fd09752 100644 --- a/packages/typo3-docs-theme/src/Inventory/Typo3InventoryRepository.php +++ b/packages/typo3-docs-theme/src/Inventory/Typo3InventoryRepository.php @@ -133,6 +133,7 @@ private function tryLoadInventoryJson(string $reducedKey, string $inventoryUrl): if ($json === []) { return false; } + /** @var array $json */ $this->loadInventoryFromJson($inventoryUrl, $json, $reducedKey); return true; } catch (ClientException) { @@ -141,7 +142,7 @@ private function tryLoadInventoryJson(string $reducedKey, string $inventoryUrl): } /** - * @param array $json + * @param array $json */ private function loadInventoryFromJson(string $inventoryUrl, array $json, string $reducedKey): void { diff --git a/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php b/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php index 4aaa6f1e9..e3f850fb3 100644 --- a/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php +++ b/packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php @@ -131,7 +131,7 @@ public function getArguments(): array } /** - * @param ViewHelperArgumentNode[] $arguments + * @param array $arguments */ public function setArguments(array $arguments): void { diff --git a/packages/typo3-guides-cli/src/Command/ConfigureCommand.php b/packages/typo3-guides-cli/src/Command/ConfigureCommand.php index d643ab940..f78b4de61 100644 --- a/packages/typo3-guides-cli/src/Command/ConfigureCommand.php +++ b/packages/typo3-guides-cli/src/Command/ConfigureCommand.php @@ -255,7 +255,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln(print_r($input->getOptions(), true)); } - $config = (string) $input->getArgument('input') . '/guides.xml'; + $inputPath = $input->getArgument('input'); + if (!is_string($inputPath)) { + $output->writeln('Input argument must be a string path'); + return Command::FAILURE; + } + $config = $inputPath . '/guides.xml'; if ($output->isVeryVerbose()) { $output->writeln(sprintf('Config: %s', $config)); diff --git a/packages/typo3-guides-cli/src/Command/InitCommand.php b/packages/typo3-guides-cli/src/Command/InitCommand.php index 203225ebb..e6c96ef21 100644 --- a/packages/typo3-guides-cli/src/Command/InitCommand.php +++ b/packages/typo3-guides-cli/src/Command/InitCommand.php @@ -46,8 +46,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int { if ($input->getOption('working-dir')) { $workdir = $input->getOption('working-dir'); - assert(is_string($workdir)); - $workdir = (string) $workdir; + if (!is_string($workdir)) { + $output->writeln('Working directory must be a string'); + return Command::INVALID; + } if (chdir($workdir)) { $output->writeln('Changed working directory to ' . getcwd() . ''); @@ -98,8 +100,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $composerInfo->getComposerName() ); $projectNameQuestion->setValidator(function ($answer) { - $answer = (string) $answer; - if (trim($answer) === '') { + if (!is_string($answer) || trim($answer) === '') { throw new \RuntimeException('The project title cannot be empty.'); } return $answer; @@ -125,7 +126,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'https://gitlab.com/' . $composerInfo->getComposerName(), ] ); - $repositoryUrl = (string) $helper->ask($input, $output, $question); + $repositoryUrl = $helper->ask($input, $output, $question); + if (!is_string($repositoryUrl)) { + $repositoryUrl = ''; + } $question = $this->createValidatedUrlQuestion( 'Where can users report issues? [%s]', @@ -150,8 +154,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $question = new Question('Do you want generate some Documentation? (yes/no) ', 'yes'); $question->setValidator(function ($answer) { - $answer = (string) $answer; - if (!in_array(strtolower($answer), [ + if (!is_string($answer) || !in_array(strtolower($answer), [ 'yes', 'y', 'no', @@ -174,7 +177,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $siteSetDefinition = ''; if (is_string($siteSet) && $siteSet !== '') { $question = new Question('Enter the path to your site set: '); - $siteSetPath = (string) $helper->ask($input, $output, $question); + $siteSetPath = $helper->ask($input, $output, $question); + if (!is_string($siteSetPath)) { + $siteSetPath = ''; + } if (is_file($siteSetPath . '/settings.definitions.yaml')) { $siteSetDefinition = $siteSetPath . '/settings.definitions.yaml'; } @@ -194,9 +200,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int mkdir($outputDir, 0o777, true); } - assert(is_string($repositoryUrl ?? '')); $editOnGitHub = null; - if (str_starts_with($repositoryUrl ?? '', 'https://github.com/')) { + if (str_starts_with($repositoryUrl, 'https://github.com/')) { $editOnGitHub = str_replace('https://github.com/', '', $repositoryUrl); } // Define your data diff --git a/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php b/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php index ea98ee2c3..195219bc9 100644 --- a/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php +++ b/packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php @@ -57,8 +57,13 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $settingsFile = (string) $input->getArgument('input') . '/Settings.cfg'; - $guidesFile = (string) $input->getArgument('input') . '/guides.xml'; + $inputPath = $input->getArgument('input'); + if (!is_string($inputPath)) { + $output->writeln('Input argument must be a string path'); + return Command::FAILURE; + } + $settingsFile = $inputPath . '/Settings.cfg'; + $guidesFile = $inputPath . '/guides.xml'; if (file_exists($guidesFile) && !$input->getOption('force')) { $output->writeln('Target file already exists in specified directory (' . $guidesFile . ')'); diff --git a/packages/typo3-guides-cli/src/XmlValidator.php b/packages/typo3-guides-cli/src/XmlValidator.php index 2b2d99c3c..066e98817 100644 --- a/packages/typo3-guides-cli/src/XmlValidator.php +++ b/packages/typo3-guides-cli/src/XmlValidator.php @@ -70,9 +70,9 @@ public function validate(): bool } // Custom error handler function within the class - private function errorHandler(mixed $errno, string $errstr): void + private function errorHandler(int $errno, string $errstr): void { - $this->errors[] = 'xxx' . (string) $errno . ': ' . $errstr; + $this->errors[] = 'xxx' . $errno . ': ' . $errstr; } public function showErrors(OutputInterface $output): void diff --git a/packages/typo3-guides-extension/src/Command/RunDecorator.php b/packages/typo3-guides-extension/src/Command/RunDecorator.php index d8c9c39e6..b97fa0051 100644 --- a/packages/typo3-guides-extension/src/Command/RunDecorator.php +++ b/packages/typo3-guides-extension/src/Command/RunDecorator.php @@ -249,7 +249,10 @@ public function renderSingleLocalization(string $availableLocalization, array $b { $localInputDirectives = []; foreach ($baseInputDirectives as $baseInputDirectiveKey => $baseInputDirectiveValue) { - $localInputDirectives[$baseInputDirectiveKey] = (string) $baseInputDirectiveValue . DIRECTORY_SEPARATOR . $availableLocalization; + if (!is_scalar($baseInputDirectiveValue)) { + continue; + } + $localInputDirectives[$baseInputDirectiveKey] = $baseInputDirectiveValue . DIRECTORY_SEPARATOR . $availableLocalization; } $output->writeln(sprintf('Trying to render %s ...', $availableLocalization)); diff --git a/packages/typo3-version-handling/src/Packagist/PackagistService.php b/packages/typo3-version-handling/src/Packagist/PackagistService.php index 92977f4dc..b8374be13 100644 --- a/packages/typo3-version-handling/src/Packagist/PackagistService.php +++ b/packages/typo3-version-handling/src/Packagist/PackagistService.php @@ -22,11 +22,22 @@ public function getComposerInfo(string $composerName): ComposerPackage // Decode JSON response $packageData = json_decode($packageResponse, true); - if (!isset($packageData['packages'][$composerName][0]) || !is_array($packageData['packages'][$composerName][0])) { + if (!is_array($packageData)) { $this->cache[$composerName] = new ComposerPackage($composerName, 'composer req ' . $composerName, 'not found'); return $this->cache[$composerName]; } - $packageVersionData = $packageData['packages'][$composerName][0]; + $packages = $packageData['packages'] ?? null; + if (!is_array($packages)) { + $this->cache[$composerName] = new ComposerPackage($composerName, 'composer req ' . $composerName, 'not found'); + return $this->cache[$composerName]; + } + $composerVersions = $packages[$composerName] ?? null; + if (!is_array($composerVersions) || !isset($composerVersions[0]) || !is_array($composerVersions[0])) { + $this->cache[$composerName] = new ComposerPackage($composerName, 'composer req ' . $composerName, 'not found'); + return $this->cache[$composerName]; + } + /** @var array $packageVersionData */ + $packageVersionData = $composerVersions[0]; $this->cache[$composerName] = $this->getComposerInfoFromJson( $packageVersionData, @@ -99,9 +110,8 @@ public function fetchPackageData(string $url): bool|string if ($this->timeoutOccurred) { return false; } - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); $errorNumber = curl_errno($ch); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 08756ffcf..065bb7d77 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -29,219 +29,3 @@ parameters: identifier: method.childParameterType count: 2 path: packages/typo3-docs-theme/src/DependencyInjection/Typo3DocsThemeExtension.php - - - - message: '#^Cannot access property \$textContent on mixed\.$#' - identifier: property.nonObject - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#1 \$categories of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildCategoryConfvals\(\) expects array\\>, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#2 \$categoryArray of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:assignConfvalsToCategories\(\) expects array\\>, array\\> given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#2 \$settings of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\\>, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#3 \$categories of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\\>, mixed given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#3 \$subject of function preg_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#4 \$labels of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#5 \$descriptions of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#6 \$categoryLabels of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfvalMenu\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#7 \$categoryArray of method T3Docs\\Typo3DocsTheme\\Directives\\SiteSetSettingsDirective\:\:buildConfval\(\) expects array\\>, array\\> given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Parameter \#7 \$value of class phpDocumentor\\Guides\\RestructuredText\\Nodes\\ConfvalNode constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Possibly invalid array key type mixed\.$#' - identifier: offsetAccess.invalidOffset - count: 3 - path: packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 3 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Cannot call method getValue\(\) on phpDocumentor\\Guides\\Nodes\\Node\|null\.$#' - identifier: method.nonObject - count: 2 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \#1 \$argumentDefinition of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getArgument\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \#1 \$array of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getString\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \#2 \$data of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getViewHelperNode\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \#3 \$sourceEdit of method T3Docs\\Typo3DocsTheme\\Directives\\ViewHelperDirective\:\:getViewHelperNode\(\) expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \$description of class T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode constructor expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \$docTags of class T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \$documentation of class T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode constructor expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php - - - - message: '#^Parameter \#2 \$json of method phpDocumentor\\Guides\\ReferenceResolvers\\Interlink\\DefaultInventoryLoader\:\:loadInventoryFromJson\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-docs-theme/src/Inventory/Typo3InventoryRepository.php - - - - message: '#^Property T3Docs\\Typo3DocsTheme\\Nodes\\ViewHelperNode\:\:\$arguments \(array\\) does not accept array\\.$#' - identifier: assign.propertyType - count: 1 - path: packages/typo3-docs-theme/src/Nodes/ViewHelperNode.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: packages/typo3-guides-cli/src/Command/ConfigureCommand.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: packages/typo3-guides-cli/src/Command/InitCommand.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 4 - path: packages/typo3-guides-cli/src/Command/InitCommand.php - - - - message: '#^Variable \$repositoryUrl on left side of \?\? always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 2 - path: packages/typo3-guides-cli/src/Command/InitCommand.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: packages/typo3-guides-cli/src/Command/MigrateSettingsCommand.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: packages/typo3-guides-cli/src/XmlValidator.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: packages/typo3-guides-extension/src/Command/RunDecorator.php - - - - message: '#^Cannot access offset ''packages'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: packages/typo3-version-handling/src/Packagist/PackagistService.php - - - - message: '#^Cannot access offset 0 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: packages/typo3-version-handling/src/Packagist/PackagistService.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: packages/typo3-version-handling/src/Packagist/PackagistService.php - - - - message: '#^Parameter \#1 \$composerJsonArray of method T3Docs\\VersionHandling\\Packagist\\PackagistService\:\:getComposerInfoFromJson\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-version-handling/src/Packagist/PackagistService.php - - - - message: '#^Parameter \#3 \$value of function curl_setopt expects bool, int given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-version-handling/src/Packagist/PackagistService.php - - - - message: '#^Parameter \#3 \$value of function curl_setopt expects non\-empty\-string, string given\.$#' - identifier: argument.type - count: 1 - path: packages/typo3-version-handling/src/Packagist/PackagistService.php From f01a7547411bfb870005a5ec30def3c393a17121 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sun, 22 Feb 2026 14:33:32 +0100 Subject: [PATCH 4/4] [BUGFIX] Restore getValue() in ViewHelperDirective to fix integration tests The previous commit incorrectly replaced getValue() with getChildren() in ViewHelperDirective. These access different properties on CompoundNode (value vs children), causing the ViewHelper description/examples/sections to be empty in rendered output. Restore getValue() with proper is_array() type narrowing to satisfy PHPStan without changing runtime behavior. --- .../src/Directives/ViewHelperDirective.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php b/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php index 71a14fa17..047c29910 100644 --- a/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php +++ b/packages/typo3-docs-theme/src/Directives/ViewHelperDirective.php @@ -171,8 +171,9 @@ private function getViewHelperNode(Directive $directive, array $data, array $sou $examples = []; if (str_contains($rawDocumentation, '```')) { $documentNode = $this->markupLanguageParser->parse($blockContext->getDocumentParserContext()->getContext(), $rawDocumentation); + $rawValue = $documentNode->getValue(); /** @var list $childNodes */ - $childNodes = array_values($documentNode->getChildren()); + $childNodes = is_array($rawValue) ? array_values($rawValue) : []; $collectionNode = new CollectionNode($childNodes); foreach ($childNodes as $childNode) { if ($childNode instanceof ParagraphNode) { @@ -185,9 +186,10 @@ private function getViewHelperNode(Directive $directive, array $data, array $sou } else { $rstContentBlockContext = new BlockContext($blockContext->getDocumentParserContext(), $rawDocumentation, false); $parsedNode = $this->startingRule->apply($rstContentBlockContext); - $collectionNode = $parsedNode instanceof CollectionNode ? $parsedNode : new CollectionNode([]); + $rawValue = $parsedNode?->getValue(); /** @var list $collectionChildren */ - $collectionChildren = $collectionNode->getChildren(); + $collectionChildren = is_array($rawValue) ? array_values($rawValue) : []; + $collectionNode = new CollectionNode($collectionChildren); foreach ($collectionChildren as $childNode) { if (!$childNode instanceof SectionNode) { $description[] = $childNode; @@ -217,13 +219,16 @@ private function getViewHelperNode(Directive $directive, array $data, array $sou $display = array_map('trim', explode(',', $directive->getOptionString('display'))); } $viewHelperId = $this->anchorNormalizer->reduceAnchor($className); + $rawDocs = $collectionNode->getValue(); + /** @var list $documentationNodes */ + $documentationNodes = is_array($rawDocs) ? $rawDocs : []; $viewHelperNode = new ViewHelperNode( id: $viewHelperId, tagName: $this->getString($data, 'tagName'), shortClassName: $shortClassName, namespace: $nameSpace, className: $className, - documentation: $collectionNode->getChildren(), + documentation: $documentationNodes, description: $description, sections: $sections, examples: $examples,