Skip to content

Commit

Permalink
tools: Add and configure PHPStan static analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
justlevine committed Nov 2, 2024
1 parent 49c3bf9 commit bd2e9c6
Show file tree
Hide file tree
Showing 15 changed files with 2,723 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ junit.xml
.wp-env.override.json
playwright.config.override.ts
phpcs.xml
phpstan.neon
phpunit.xml
phpunit-watcher.yml
.tool-versions
Expand Down
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
},
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"composer/installers": true
"composer/installers": true,
"phpstan/extension-installer": true
},
"lock": false
},
Expand All @@ -32,7 +33,10 @@
"sirbrillig/phpcs-variable-analysis": "^2.8",
"spatie/phpunit-watcher": "^1.23",
"yoast/phpunit-polyfills": "^1.1.0",
"gutenberg/gutenberg-coding-standards": "@dev"
"gutenberg/gutenberg-coding-standards": "@dev",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^1.12",
"szepeviktor/phpstan-wordpress": "^1.3"
},
"repositories": [
{
Expand All @@ -47,6 +51,7 @@
"composer/installers": "^1.0 || ^2.0"
},
"scripts": {
"analyse": "phpstan analyse -v",
"format": "phpcbf --standard=phpcs.xml.dist --report-summary --report-source",
"lint": "phpcs --standard=phpcs.xml.dist",
"test": "phpunit",
Expand Down
4 changes: 4 additions & 0 deletions docs/contributors/folder-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The following snippet explains how the Gutenberg repository is structured omitti
├── .markdownlintignore
├── .npmpackagejsonlintrc.json
├── phpcs.xml.dist
├── phpstan.neon.dist
│ Dot files and config files used to configure the various linting tools
│ used in the repository (PHP, JS, styles...).
Expand Down Expand Up @@ -141,5 +142,8 @@ The following snippet explains how the Gutenberg repository is structured omitti
├── tools/eslint
│ Configuration files for the ESLint linter.
├── tools/phpstan
│ Configuration files for the PHPStan analysis.
├── tools/webpack
│ Configuration files for the webpack build.
34 changes: 34 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# PHPStan configuration for Gutenberg
#
# To overload this configuration, copy this file to phpstan.neon and adjust as needed.
#
# https://phpstan.org/config-reference

includes:
# The WordPress Core configuration file includes the base configuration for the WordPress codebase.
- tools/phpstan/base.neon
# The baseline file includes preexisting errors in the codebase that should be ignored.
# https://phpstan.org/user-guide/baseline
- tools/phpstan/baseline/level-0.php
- tools/phpstan/baseline/level-1.php
- tools/phpstan/baseline/level-2.php
- tools/phpstan/baseline/level-3.php
- tools/phpstan/baseline/level-4.php
- tools/phpstan/baseline/level-5.php
- tools/phpstan/baseline/level-6.php
- tools/phpstan/baseline/level-7.php

parameters:
level: 7

treatPhpDocTypesAsCertain: false

ignoreErrors:
# @TODO: need to discover these symbols.
- '#Function gutenberg_.* not found.#'

# Level 6:

# WPCS syntax for iterable types is not supported:
-
identifier: missingType.iterableValue
26 changes: 26 additions & 0 deletions tools/phpstan/base.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Base PHPStan configuration for Gutenberg
#
# This is kept separate from the main PHPStan configuration file to allow for easy overloading while baseline errors are being fixed.
#
# https://phpstan.org/config-reference

parameters:
# What directories and files should be scanned.
paths:
- ../../gutenberg.php
- ../../post-content.php
- ../../lib
bootstrapFiles:
- bootstrap.php
- ../../gutenberg.php
scanFiles:
- ../../gutenberg.php
- ../../post-content.php
scanDirectories:
- ../../lib
- ../../packages
excludePaths:
analyse:
# Exclude files maintained in WordPress Core and backported to Gutenberg.
- ../../lib/compat/wordpress-*/html-api/*
- ../../packages/block-serialization-spec-parser/parser
65 changes: 65 additions & 0 deletions tools/phpstan/baseline/level-0.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php declare(strict_types = 1);

$ignoreErrors = [];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Callback expects 1 parameter, \\$accepted_args is set to 2\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/block-supports/block-style-variations.php',
];
$ignoreErrors[] = [
// identifier: return.missing
'message' => '#^Function gutenberg_tinycolor_string_to_rgb\\(\\) should return array but return statement is missing\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/block-supports/duotone.php',
];
$ignoreErrors[] = [
// identifier: return.missing
'message' => '#^Method WP_Duotone_Gutenberg\\:\\:get_selector\\(\\) should return string but return statement is missing\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/class-wp-duotone-gutenberg.php',
];
$ignoreErrors[] = [
// identifier: return.missing
'message' => '#^Method WP_Theme_JSON_Gutenberg\\:\\:should_override_preset\\(\\) should return bool but return statement is missing\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/class-wp-theme-json-gutenberg.php',
];
$ignoreErrors[] = [
// identifier: phpDoc.parseError
'message' => '#^One or more @param tags has an invalid name or invalid syntax\\.$#',
'count' => 4,
'path' => __DIR__ . '/../../../lib/class-wp-theme-json-resolver-gutenberg.php',
];
$ignoreErrors[] = [
// identifier: class.notFound
'message' => '#^Call to static method get_stores\\(\\) on an unknown class WP_Style_Engine_CSS_Rules_Store_Gutenberg\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/client-assets.php',
];
$ignoreErrors[] = [
// identifier: new.static
'message' => '#^Unsafe usage of new static\\(\\)\\.$#',
'count' => 2,
'path' => __DIR__ . '/../../../lib/compat/wordpress-6.6/class-gutenberg-token-map-6-6.php',
];
$ignoreErrors[] = [
// identifier: return.missing
'message' => '#^Method Gutenberg_REST_Templates_Controller_6_7\\:\\:get_wp_templates_author_text_field\\(\\) should return string but return statement is missing\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/compat/wordpress-6.7/class-gutenberg-rest-templates-controller-6-7.php',
];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Callback expects 1 parameter, \\$accepted_args is set to 2\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/experimental/kses-allowed-html.php',
];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Callback expects 1 parameter, \\$accepted_args is set to 2\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/rest-api.php',
];

return ['parameters' => ['ignoreErrors' => $ignoreErrors]];
59 changes: 59 additions & 0 deletions tools/phpstan/baseline/level-1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php declare(strict_types = 1);

$ignoreErrors = [];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Function remove_filter invoked with 4 parameters, 2\\-3 required\\.$#',
'count' => 4,
'path' => __DIR__ . '/../../../lib/block-supports/elements.php',
];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Function remove_filter invoked with 4 parameters, 2\\-3 required\\.$#',
'count' => 3,
'path' => __DIR__ . '/../../../lib/block-supports/layout.php',
];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Function remove_filter invoked with 4 parameters, 2\\-3 required\\.$#',
'count' => 2,
'path' => __DIR__ . '/../../../lib/block-supports/settings.php',
];
$ignoreErrors[] = [
// identifier: variable.undefined
'message' => '#^Variable \\$filter_id might not be defined\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/class-wp-duotone-gutenberg.php',
];
$ignoreErrors[] = [
// identifier: isset.variable
'message' => '#^Variable \\$area in isset\\(\\) always exists and is not nullable\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/compat/wordpress-6.6/block-template-utils.php',
];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Class WP_Webfonts constructor invoked with 1 parameter, 0 required\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/experimental/font-face/bc-layer/webfonts-deprecations.php',
];
$ignoreErrors[] = [
// identifier: arguments.count
'message' => '#^Function gutenberg_url invoked with 2 parameters, 1 required\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/experimental/posts/load.php',
];
$ignoreErrors[] = [
// identifier: variable.undefined
'message' => '#^Variable \\$cache_key might not be defined\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/global-styles-and-settings.php',
];
$ignoreErrors[] = [
// identifier: variable.undefined
'message' => '#^Variable \\$cached might not be defined\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../../lib/global-styles-and-settings.php',
];

return ['parameters' => ['ignoreErrors' => $ignoreErrors]];
Loading

0 comments on commit bd2e9c6

Please sign in to comment.