Commit 997722b8 authored by Stefan Bürk's avatar Stefan Bürk Committed by Christian Kuhn
Browse files

[TASK] Revamp phpstan config and handling

We currently have the situation that phpstan is
hard to update and maintain due to the phpstan
config file that sets very specific rulesets.

This is unfortunate since phpstan tends to change
and rename rules at will.

The general usage API of phpstan is basically as
follows: Have a slim config file that sets the
basic level. Then maintain a 'baseline' file that
lists violations, using the --generate-baseline
command option. The todo job for people working
on phpstan errors is then to look at the baseline
file, pick up some issues, fix them, then re-generate
baseline. When baseline is small enough, the level
is raised, a new baseline is generated, and the
fix-job starts again.

The patch does exactly this: The existing config
is dropped and runTests.sh receives a command to
generate baseline.

With this in place, we can easily raise phpstan
to a PHP 8.1 compatible version:

> composer req friendsoftypo3/phpstan-typo3:"^0.9.0" --dev
> composer req phpstan/phpstan:"^1.4.3" --dev
> Build/Script/runTests.sh -s phpstanGenerateBaseline

This initialy adds about 4000 ignores to the baseline
with level 3, but we can reduce this drastically with
just a couple of dedicated patches, soon.

The config is heavily streamlined and for instance does
*not* ignore tests anymore, which actually finds a ton
of misuses and bugs within tests and classes.

Resolves: #96675
Releases: main, 11.5
Change-Id: I0e7ff7aa796e59a2c5eedde0b673f741f8b87dea
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/73037


Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Daniel Goerz's avatarDaniel Goerz <daniel.goerz@posteo.de>
Tested-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: Daniel Goerz's avatarDaniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 3f89c5a3
......@@ -111,6 +111,7 @@ Options:
- lintHtml: HTML linting
- listExceptionCodes: list core exception codes in JSON format
- phpstan: phpstan tests
- phpstanGenerateBaseline: regenerate phpstan baseline, handy after phpstan updates
- unit (default): PHP unit tests
- unitDeprecated: deprecated PHP unit tests
- unitJavascript: JavaScript unit tests
......@@ -712,13 +713,17 @@ case ${TEST_SUITE} in
docker-compose down
;;
phpstan)
# @todo remove hardcoded php version when phpstan is raised and runnable with PHP8.1
PHP_VERSION="8.0"
setUpDockerComposeDotEnv
docker-compose run phpstan
SUITE_EXIT_CODE=$?
docker-compose down
;;
phpstanGenerateBaseline)
setUpDockerComposeDotEnv
docker-compose run phpstan_generate_baseline
SUITE_EXIT_CODE=$?
docker-compose down
;;
unit)
setUpDockerComposeDotEnv
docker-compose run unit
......
......@@ -81,8 +81,7 @@ lint scss ts html:
- Build/Scripts/runTests.sh -s lintTypescript
- Build/Scripts/runTests.sh -s lintHtml
# @todo Hardcoded enforced to PHP8.0 in Build/Scripts/runTests.sh - change this after phpstan:^1.2 raised.
phpstan php 8.0:
phpstan php 8.1:
stage: integrity
needs: []
only:
......
......@@ -60,8 +60,7 @@ lint scss ts html pre-merge:
- Build/Scripts/runTests.sh -s lintTypescript
- Build/Scripts/runTests.sh -s lintHtml
# @todo Hardcoded enforced to PHP8.0 in Build/Scripts/runTests.sh - change this after phpstan:^1.2 raise.
phpstan php 8.0 pre-merge:
phpstan php 8.1 pre-merge:
stage: main
except:
refs:
......
rules:
- PHPStan\Rules\Arrays\AppendedArrayItemTypeRule
- PHPStan\Rules\Arrays\IterableInForeachRule
- PHPStan\Rules\Arrays\OffsetAccessAssignmentRule
- PHPStan\Rules\Arrays\OffsetAccessAssignOpRule
- PHPStan\Rules\Arrays\OffsetAccessValueAssignmentRule
- PHPStan\Rules\Arrays\UnpackIterableInArrayRule
- PHPStan\Rules\Functions\ArrowFunctionReturnTypeRule
- PHPStan\Rules\Functions\ClosureReturnTypeRule
- PHPStan\Rules\Generators\YieldTypeRule
# - PHPStan\Rules\Methods\ReturnTypeRule
- PHPStan\Rules\Properties\DefaultValueTypesAssignedToPropertiesRule
- PHPStan\Rules\Properties\TypesAssignedToPropertiesRule
- PHPStan\Rules\Variables\ThrowTypeRule
- PHPStan\Rules\Variables\VariableCloningRule
conditionalTags:
PHPStan\Rules\Arrays\ArrayDestructuringRule:
phpstan.rules.rule: %featureToggles.arrayDestructuring%
PHPStan\Rules\Exceptions\ThrowsVoidFunctionWithExplicitThrowPointRule:
phpstan.rules.rule: %featureToggles.throwsVoid%
PHPStan\Rules\Exceptions\ThrowsVoidMethodWithExplicitThrowPointRule:
phpstan.rules.rule: %featureToggles.throwsVoid%
parameters:
checkPhpDocMethodSignatures: true
services:
-
class: PHPStan\Rules\Arrays\AppendedArrayKeyTypeRule
arguments:
checkUnionTypes: %checkUnionTypes%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Arrays\ArrayDestructuringRule
-
class: PHPStan\Rules\Arrays\InvalidKeyInArrayDimFetchRule
arguments:
reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Arrays\InvalidKeyInArrayItemRule
arguments:
reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Arrays\NonexistentOffsetInArrayDimFetchRule
arguments:
reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Exceptions\ThrowsVoidFunctionWithExplicitThrowPointRule
arguments:
exceptionTypeResolver: @exceptionTypeResolver
missingCheckedExceptionInThrows: %exceptions.check.missingCheckedExceptionInThrows%
-
class: PHPStan\Rules\Exceptions\ThrowsVoidMethodWithExplicitThrowPointRule
arguments:
exceptionTypeResolver: @exceptionTypeResolver
missingCheckedExceptionInThrows: %exceptions.check.missingCheckedExceptionInThrows%
# -
# class: PHPStan\Rules\Functions\ReturnTypeRule
# arguments:
# functionReflector: @betterReflectionFunctionReflector
#
# tags:
# - phpstan.rules.rule
-
class: PHPStan\Rules\Generators\YieldFromTypeRule
arguments:
reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Generators\YieldInGeneratorRule
arguments:
reportMaybes: %reportMaybes%
tags:
- phpstan.rules.rule
includes:
- phpstan.level3.neon
rules:
# - PHPStan\Rules\Arrays\DeadForeachRule
# - PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule
- PHPStan\Rules\DeadCode\NoopRule
# - PHPStan\Rules\DeadCode\UnreachableStatementRule
- PHPStan\Rules\Exceptions\DeadCatchRule
# - PHPStan\Rules\Functions\CallToFunctionStamentWithoutSideEffectsRule
- PHPStan\Rules\Methods\CallToConstructorStatementWithoutSideEffectsRule
- PHPStan\Rules\Methods\CallToMethodStamentWithoutSideEffectsRule
- PHPStan\Rules\Methods\CallToStaticMethodStamentWithoutSideEffectsRule
- PHPStan\Rules\Methods\NullsafeMethodCallRule
- PHPStan\Rules\Properties\NullsafePropertyFetchRule
- PHPStan\Rules\TooWideTypehints\TooWideArrowFunctionReturnTypehintRule
- PHPStan\Rules\TooWideTypehints\TooWideClosureReturnTypehintRule
- PHPStan\Rules\TooWideTypehints\TooWideFunctionReturnTypehintRule
conditionalTags:
PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule:
phpstan.rules.rule: %featureToggles.preciseExceptionTracking%
PHPStan\Rules\Exceptions\OverwrittenExitPointByFinallyRule:
phpstan.rules.rule: %featureToggles.preciseExceptionTracking%
PHPStan\Rules\DeadCode\UnusedPrivateConstantRule:
phpstan.rules.rule: %featureToggles.unusedClassElements%
PHPStan\Rules\DeadCode\UnusedPrivateMethodRule:
phpstan.rules.rule: %featureToggles.unusedClassElements%
PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule:
phpstan.rules.rule: %featureToggles.unusedClassElements%
parameters:
checkAdvancedIsset: true
services:
-
class: PHPStan\Rules\Classes\ImpossibleInstanceOfRule
arguments:
checkAlwaysTrueInstanceof: %checkAlwaysTrueInstanceof%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\BooleanAndConstantConditionRule
# arguments:
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\BooleanOrConstantConditionRule
# arguments:
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\BooleanNotConstantConditionRule
# arguments:
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
-
class: PHPStan\Rules\DeadCode\UnusedPrivateConstantRule
-
class: PHPStan\Rules\DeadCode\UnusedPrivateMethodRule
-
class: PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule
arguments:
alwaysWrittenTags: %propertyAlwaysWrittenTags%
alwaysReadTags: %propertyAlwaysReadTags%
checkUninitializedProperties: %checkUninitializedProperties%
# -
# class: PHPStan\Rules\Comparison\ElseIfConstantConditionRule
# arguments:
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\IfConstantConditionRule
# arguments:
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\ImpossibleCheckTypeFunctionCallRule
# arguments:
# checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeMethodCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeStaticMethodCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\MatchExpressionRule
arguments:
checkAlwaysTrueStrictComparison: %checkAlwaysTrueStrictComparison%
tags:
- phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\StrictComparisonOfDifferentTypesRule
# arguments:
# checkAlwaysTrueStrictComparison: %checkAlwaysTrueStrictComparison%
#
# tags:
# - phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\TernaryOperatorConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\UnreachableIfBranchesRule
# arguments:
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
# -
# class: PHPStan\Rules\Comparison\UnreachableTernaryElseBranchRule
# arguments:
# treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
#
# tags:
# - phpstan.rules.rule
-
class: PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule
-
class: PHPStan\Rules\Exceptions\DeadCatchRule
arguments:
bleedingEdge: %featureToggles.bleedingEdge%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Exceptions\OverwrittenExitPointByFinallyRule
-
class: PHPStan\Rules\TooWideTypehints\TooWideMethodReturnTypehintRule
arguments:
checkProtectedAndPublicMethods: %checkTooWideReturnTypesInProtectedAndPublicMethods%
tags:
- phpstan.rules.rule
includes:
- phpstan.level4.neon
conditionalTags:
PHPStan\Rules\Functions\RandomIntParametersRule:
phpstan.rules.rule: %featureToggles.randomIntParameters%
PHPStan\Rules\DateTimeInstantiationRule:
phpstan.rules.rule: %featureToggles.dateTimeInstantiation%
parameters:
checkFunctionArgumentTypes: true
checkArgumentsPassedByReference: true
services:
-
class: PHPStan\Rules\Functions\RandomIntParametersRule
arguments:
reportMaybes: %reportMaybes%
-
class: PHPStan\Rules\DateTimeInstantiationRule
includes:
- phpstan.level5.neon
parameters:
# checkGenericClassInNonGenericObjectType: true
checkMissingIterableValueType: true
# checkMissingVarTagTypehint: true
checkMissingTypehints: true
rules:
- PHPStan\Rules\Constants\MissingClassConstantTypehintRule
- PHPStan\Rules\Functions\MissingFunctionParameterTypehintRule
- PHPStan\Rules\Functions\MissingFunctionReturnTypehintRule
# - PHPStan\Rules\Methods\MissingMethodParameterTypehintRule
# - PHPStan\Rules\Methods\MissingMethodReturnTypehintRule
# - PHPStan\Rules\Properties\MissingPropertyTypehintRule
includes:
- phpstan.level6.neon
parameters:
checkUnionTypes: true
# reportMaybes: true
includes:
- phpstan.level7.neon
parameters:
checkNullables: true
<?php
declare(strict_types=1);
$config = [];
if (PHP_MAJOR_VERSION === 8) {
$config['parameters']['ignoreErrors'] = [
[
'message' => '#^Parameter \\#1 \\$image of function imagedestroy expects GdImage, GdImage\\|false given\\.$#',
'path' => '%currentWorkingDirectory%/typo3/sysext/install/Classes/SystemEnvironment/Check.php',
'count' => 3,
],
[
'message' => '#^Parameter \\#6 \\$color of function imagefilledrectangle expects int, int\\|false given\\.$#',
'path' => '%currentWorkingDirectory%/typo3/sysext/install/Classes/Controller/EnvironmentController.php',
'count' => 4,
],
[
'message' => '#^Parameter \\#1 \\$image of function imagefilledrectangle expects GdImage, GdImage\\|false given\\.$#',
'path' => '%currentWorkingDirectory%/',
'count' => 4,
],
[
'message' => '#^Parameter \\#1 \\$image of function imagegif expects GdImage, GdImage\\|false given\\.$#',
'path' => '%currentWorkingDirectory%/',
'count' => 1,
],
[
'message' => '#^Parameter \\#6 \\$color of function imagettftext expects int, int\\|false given\\.$#',
'path' => '%currentWorkingDirectory%/',
'count' => 1,
],
[
'message' => '#^Parameter \\#1 \\$image of function imagettftext expects GdImage, GdImage\\|false given\\.$#',
'path' => '%currentWorkingDirectory%/',
'count' => 1,
],
[
'message' => '#^Parameter \\#1 \\$image of function imagecolorallocate expects GdImage, GdImage\\|false given\\.$#',
'path' => '%currentWorkingDirectory%/',
'count' => 6,
],
[
'message' => '#^Parameter \\#1 \\$semaphore of function sem_release expects SysvSemaphore, resource\\|SysvSemaphore given\\.$#',
'path' => '%currentWorkingDirectory%/typo3/sysext/core/Classes/Locking/SemaphoreLockStrategy.php',
'count' => 1,
],
[
'message' => '#^Parameter \\#1 \\$semaphore of function sem_remove expects SysvSemaphore, resource\\|SysvSemaphore given\\.$#',
'path' => '%currentWorkingDirectory%/typo3/sysext/core/Classes/Locking/SemaphoreLockStrategy.php',
'count' => 1,
],
[
'message' => '#^Parameter \\#1 \\$separator of function explode expects non-empty-string, string given\\.$#',
'path' => '%currentWorkingDirectory%/',
'count' => 7,
],
[
'message' => '#^Ternary operator condition is always true.$#',
'path' => '%currentWorkingDirectory%/typo3/sysext/core/Classes/Utility/GeneralUtility.php',
'count' => 3,
],
];
}
return $config;
This diff is collapsed.
<?php
// testing-framework defines this, used in various tests.
define('ORIGINAL_ROOT', dirname(__FILE__, 2) . '/');
includes:
- phpstan-baseline.neon
- ../../vendor/friendsoftypo3/phpstan-typo3/extension.neon
parameters:
# Use local .cache dir instead of /tmp
tmpDir: ../../.cache/phpstan
parallel:
# Don't be overly greedy on machines with more CPU's to be a good neighbor especially on CI
maximumNumberOfProcesses: 5
level: 3
bootstrapFiles:
- phpstan-constants.php
paths:
- ../../typo3/sysext/
excludePaths:
# Checking acceptance Cest and Support files is cumbersome due to codeception dynamic mixin generation
- ../../typo3/sysext/core/Tests/Acceptance/*
# These test fixtures *could* be streamlined but currently generate lots of failures
- ../../typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/*
# ext_emconf.php get the $_EXTKEY set from outsite. We'll ignore all of them
- ../../typo3/sysext/*/ext_emconf.php
......@@ -1230,7 +1230,22 @@ services:
set -x
fi
mkdir -p .cache
bin/phpstan analyse --no-progress --no-interaction --memory-limit 4G ${TEST_FILE}
bin/phpstan analyse -c Build/phpstan/phpstan.neon --no-progress --no-interaction --memory-limit 4G ${TEST_FILE}
"
phpstan_generate_baseline:
image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest
user: "${HOST_UID}"
volumes:
- ${CORE_ROOT}:${CORE_ROOT}
working_dir: ${CORE_ROOT}
command: >
/bin/sh -c "
if [ ${SCRIPT_VERBOSE} -eq 1 ]; then
set -x
fi
mkdir -p .cache
bin/phpstan analyse -c Build/phpstan/phpstan.neon --no-progress --no-interaction --memory-limit 4G --generate-baseline=Build/phpstan/phpstan-baseline.neon
"
unit:
......
......@@ -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": "b258e92dde2904803082b1cf33525642",
"content-hash": "6db47f614f848ad71d3541caab8e88a9",
"packages": [
{
"name": "bacon/bacon-qr-code",
......@@ -5984,21 +5984,21 @@
},
{
"name": "friendsoftypo3/phpstan-typo3",
"version": "0.8.1",
"version": "0.9.0",
"source": {
"type": "git",
"url": "https://github.com/FriendsOfTYPO3/phpstan-typo3.git",
"reference": "5060372cb8d05673c6119e9a5443e9f11a49baa9"
"reference": "4d3f45b170983097ca355e9f81d073f34cb130aa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/FriendsOfTYPO3/phpstan-typo3/zipball/5060372cb8d05673c6119e9a5443e9f11a49baa9",
"reference": "5060372cb8d05673c6119e9a5443e9f11a49baa9",
"url": "https://api.github.com/repos/FriendsOfTYPO3/phpstan-typo3/zipball/4d3f45b170983097ca355e9f81d073f34cb130aa",
"reference": "4d3f45b170983097ca355e9f81d073f34cb130aa",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^0.12.44"
"php": "^7.2 || ^8.0 || ^8.1",
"phpstan/phpstan": "^0.12.99 || ^1.2.0"
},
"require-dev": {
"ergebnis/composer-normalize": "^2.3",
......@@ -6021,9 +6021,9 @@
"description": "TYPO3 rules for PHPStan",
"support": {
"issues": "https://github.com/FriendsOfTYPO3/phpstan-typo3/issues",
"source": "https://github.com/FriendsOfTYPO3/phpstan-typo3/tree/0.8.1"
"source": "https://github.com/FriendsOfTYPO3/phpstan-typo3/tree/0.9.0"
},
"time": "2021-05-31T08:45:47+00:00"
"time": "2022-01-17T07:24:51+00:00"
},
{
"name": "mikey179/vfsstream",
......@@ -6484,16 +6484,16 @@
},
{
"name": "phpstan/phpstan",
"version": "0.12.99",
"version": "1.4.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7"
"reference": "89d10839dbfc95eeb7da656578b4a899ad2b59b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7",
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/89d10839dbfc95eeb7da656578b4a899ad2b59b1",
"reference": "89d10839dbfc95eeb7da656578b4a899ad2b59b1",
"shasum": ""
},
"require": {
......@@ -6509,7 +6509,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.12-dev"
"dev-master": "1.4-dev"
}
},
"autoload": {
......@@ -6524,7 +6524,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/0.12.99"
"source": "https://github.com/phpstan/phpstan/tree/1.4.3"
},
"funding": [
{
......@@ -6544,7 +6544,7 @@
"type": "tidelift"
}
],
"time": "2021-09-12T20:09:55+00:00"
"time": "2022-01-28T16:27:17+00:00"
},
{
"name": "phpunit/php-code-coverage",
......
includes:
- vendor/friendsoftypo3/phpstan-typo3/extension.neon
- Build/phpstan.level8.neon
- Build/phpstan.php8config.php
# Include bleeding edge rules if necessary but do not commit