diff --git a/composer.json b/composer.json index 1586264a73f78..70928671aa6e3 100644 --- a/composer.json +++ b/composer.json @@ -54,6 +54,7 @@ "phpcompatibility/phpcompatibility-wp": "~2.1.3", "phpstan/phpstan": "2.3.4", "phpstan/phpstan-phpunit": "2.1.1", + "phpstan/phpstan-strict-rules": "2.1.1", "yoast/phpunit-polyfills": "^1.1.0" }, "config": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 655bb9bbc807c..9baee0ab6b3fd 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,6 +1,8 @@ # PHPStan configuration for WordPress Core. # -# To overload this configuration, copy this file to phpstan.neon and adjust as needed. +# To override this configuration, create a phpstan.neon which includes this file and adjusts +# its parameters as needed, rather than copying it, so that the copy does not go stale. See +# tests/phpstan/README.md for examples. # # https://phpstan.org/config-reference @@ -58,11 +60,27 @@ includes: - tests/phpstan/baselines/variable.undefined.neon # phpstan:baselines end +# Rules from phpstan-strict-rules which core already complies with, or which enforce the +# WordPress Coding Standards. The others are not enabled until the code complies with them, +# but they can be enabled in a local phpstan.neon to work toward that. See +# tests/phpstan/README.md. +# +# These are registered individually, rather than by including phpstan-strict-rules' rules.neon +# with only these enabled, because that file also makes several PHPStan parameters stricter +# even when its rules are disabled. +# https://github.com/phpstan/phpstan-strict-rules +rules: + - PHPStan\Rules\DisallowedConstructs\DisallowedBacktickRule + - PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule + - PHPStan\Rules\Functions\ClosureUsesThisRule + - PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule + parameters: # https://phpstan.org/user-guide/rule-levels level: 5 reportUnmatchedIgnoredErrors: true + # The following ignored errors are not intended to be fixed, as distinct from the baselines # included above. # diff --git a/tests/phpstan/HashNotationVisitor.php b/tests/phpstan/HashNotationVisitor.php index be61c13915ed0..d5be69a572d64 100644 --- a/tests/phpstan/HashNotationVisitor.php +++ b/tests/phpstan/HashNotationVisitor.php @@ -484,7 +484,12 @@ private function split_tags( string $text ): array { $tags = array(); $current = null; - foreach ( preg_split( '#\R#', (string) $body ) ?: array() as $line ) { + $lines = preg_split( '#\R#', (string) $body ); + if ( false === $lines ) { + $lines = array(); + } + + foreach ( $lines as $line ) { $line = (string) preg_replace( '#^\s*\* ?#', '', $line, 1 ); if ( preg_match( '#^@([a-zA-Z][a-zA-Z0-9_-]*)[ \t]*(.*)$#', $line, $matches ) === 1 ) { diff --git a/tests/phpstan/README.md b/tests/phpstan/README.md index 6734e780223ca..e263cd409036d 100644 --- a/tests/phpstan/README.md +++ b/tests/phpstan/README.md @@ -4,7 +4,7 @@ PHPStan is a static analysis tool for PHP that checks your code for errors witho ## Running the tests -PHPStan requires PHP and Composer dependencies to be installed. +PHPStan requires PHP and Composer dependencies to be installed. Since there is no `composer.lock`, run `composer update` after pulling changes to `composer.json`, such as the addition of a PHPStan extension, or PHPStan will fail to load the configuration. If you don't already have an environment ready, you can set one up by following [these instructions](https://github.com/WordPress/wordpress-develop/blob/master/README.md). @@ -54,9 +54,55 @@ Some errors cannot be ignored, and so cannot be reported as warnings either. The ## The PHPStan configuration -The PHPStan configuration file is located at [`phpstan.neon.dist`](../../phpstan.neon.dist). +The PHPStan configuration file is located at [`phpstan.neon.dist`](../../phpstan.neon.dist). It sets the standards the code is held to: the rule level, the baselines, the errors ignored as not actionable, and the [strict rules](#strict-rules) enabled. It includes [`base.neon`](base.neon), which configures what PHPStan needs to know to analyze the codebase at all, such as the [WordPress-specific extensions](#wordpress-specific-extensions), the paths analyzed, and the supported PHP versions. -You can create a local copy at `phpstan.neon` to override the default configuration. +To override the default configuration, create a `phpstan.neon` file in the repository root, which PHPStan reads in place of `phpstan.neon.dist`. Include `phpstan.neon.dist` in it rather than copying it, so the override only states what it changes and keeps up with changes to the default configuration. For example, to analyze at a stricter rule level: + +```neon +includes: + - phpstan.neon.dist + +parameters: + level: 10 +``` + +### Strict rules + +`phpstan.neon.dist` enables only the rules from [phpstan-strict-rules](https://github.com/phpstan/phpstan-strict-rules) which the code already complies with, registering each one individually. It does not include the package's `rules.neon`, since that file also makes several PHPStan parameters stricter, such as `checkDynamicProperties` and `reportMaybesInMethodSignatures`, even when its rules are disabled. + +To work toward complying with more of them, include `rules.neon` in your `phpstan.neon`. It enables every rule and makes those parameters stricter, unless `strictRules.allRules` is set to `false`, in which case only the rules set to `true` are enabled but the parameters are still made stricter. Either way, disable the rules `phpstan.neon.dist` already registers, or each error they report is reported twice: + +```neon +includes: + - phpstan.neon.dist + - vendor/phpstan/phpstan-strict-rules/rules.neon + +parameters: + strictRules: + allRules: false + disallowedLooseComparison: true + overwriteVariablesWithLoop: true + + # Already registered in phpstan.neon.dist. + disallowedBacktick: false + disallowedShortTernary: false + closureUsesThis: false + matchingInheritedMethodNames: false +``` + +The rules for `while` and `do`-`while` conditions are only enabled with [bleeding edge](https://phpstan.org/blog/what-is-bleeding-edge) (as are those for unary `+` and `-` operands), so `booleansInConditions: true` alone does not cover loops. Set `booleansInLoopConditions: true` to enable them without it. + +### Seeing every error + +The baselines and other ignored errors cannot be removed by leaving out an include, but a key ending in `!` replaces the value it would otherwise be merged with. So to see every error, including those the baselines suppress: + +```neon +includes: + - phpstan.neon.dist + +parameters: + ignoreErrors!: [] +``` For more information about configuring PHPStan, see the [PHPStan documentation's Config reference](https://phpstan.org/config-reference). diff --git a/tests/phpstan/base.neon b/tests/phpstan/base.neon index a2f186720f018..be98b5e1503cc 100644 --- a/tests/phpstan/base.neon +++ b/tests/phpstan/base.neon @@ -1,6 +1,12 @@ # Base PHPStan configuration for WordPress Core. # -# This is kept separate from the main PHPStan configuration file to allow for easy overloading while baseline errors are being fixed. +# This holds what PHPStan needs to know to analyze the WordPress codebase: the extensions for +# WordPress-specific conventions, the paths analyzed and excluded, the supported PHP versions, +# and the constants whose values vary. It rarely changes. +# +# The standards the code is held to, which are the rule level, the baselines, the errors +# ignored as not actionable, and the strict rules enabled, are in phpstan.neon.dist instead, +# so they can be read and reviewed apart from all of this. # # https://phpstan.org/config-reference