$npx -y skills add Lonsdale201/wp-agent-skills --skill wp-phpstan-static-analysisRun PHPStan static analysis on a WordPress plugin or theme using szepeviktor/phpstan-wordpress. Covers the composer dev-dependencies and what is pulled transitively (phpstan/phpstan ^2.0, php-stubs/wordpress-stubs), the optional phpstan/extension-installer that auto-regis
| 1 | # PHPStan static analysis for WordPress |
| 2 | |
| 3 | PHPStan catches type errors, dead code, and bad calls without running anything. It doesn't load WordPress, so it needs **stubs** for core's functions/classes — `szepeviktor/phpstan-wordpress` provides those plus WP-aware rules. This skill wires it up and tames the WP-specific noise. PHPCS (`wp-phpcs-coding-standards`) covers style; PHPStan covers types/logic — run both. |
| 4 | |
| 5 | ## When to use this skill |
| 6 | |
| 7 | - Adding static analysis to a plugin/theme. |
| 8 | - Writing or reviewing `phpstan.neon` / `phpstan.neon.dist`. |
| 9 | - Raising the analysis level on existing code (and baselining the backlog). |
| 10 | - Killing WP-specific false positives (`apply_filters` arg counts, `is_wp_error()`, `$wpdb`, conditional constants). |
| 11 | |
| 12 | ## Install |
| 13 | |
| 14 | ```bash |
| 15 | composer require --dev szepeviktor/phpstan-wordpress |
| 16 | composer require --dev phpstan/extension-installer # recommended: auto-registers the extension |
| 17 | ``` |
| 18 | |
| 19 | `szepeviktor/phpstan-wordpress` (2.0.3) pulls in **`phpstan/phpstan` `^2.0`** and **`php-stubs/wordpress-stubs`** transitively — you don't add those yourself. It requires PHPStan 2.0+. |
| 20 | |
| 21 | `phpstan/extension-installer` is a Composer plugin that auto-includes any installed `phpstan-extension` package's config, so you don't hand-wire the include. Composer 2.2+ needs it in `allow-plugins`: |
| 22 | |
| 23 | ```json |
| 24 | { |
| 25 | "config": { |
| 26 | "allow-plugins": { |
| 27 | "phpstan/extension-installer": true |
| 28 | } |
| 29 | } |
| 30 | } |
| 31 | ``` |
| 32 | |
| 33 | ## Configure (`phpstan.neon.dist`) |
| 34 | |
| 35 | **With `extension-installer`**, the config is minimal — the extension auto-registers: |
| 36 | |
| 37 | ```neon |
| 38 | parameters: |
| 39 | level: 5 |
| 40 | paths: |
| 41 | - my-plugin.php |
| 42 | - includes/ |
| 43 | ``` |
| 44 | |
| 45 | **Without `extension-installer`**, include the extension manually (exact path, verified): |
| 46 | |
| 47 | ```neon |
| 48 | includes: |
| 49 | - vendor/szepeviktor/phpstan-wordpress/extension.neon |
| 50 | parameters: |
| 51 | level: max |
| 52 | paths: |
| 53 | - my-plugin.php |
| 54 | - includes/ |
| 55 | ignoreErrors: |
| 56 | # WP filter functions use func_get_args(); calling with extra args is fine. |
| 57 | - '#^Function apply_filters(_ref_array)? invoked with [34567] parameters, 2 required\.$#' |
| 58 | ``` |
| 59 | |
| 60 | You normally do **not** set `bootstrapFiles` for WP core — the extension already loads `php-stubs/wordpress-stubs`. Add `bootstrapFiles` only for extra stubs (see WooCommerce below) or your own define-shims. |
| 61 | |
| 62 | ### Levels |
| 63 | |
| 64 | PHPStan has **levels 0–10** (0 loosest, 10 strictest; level 10 was added in PHPStan 2.0). `max` is an alias for the highest. Start where the code passes (often 5), commit that, then raise one level at a time. Don't jump to `max` on a legacy plugin — baseline instead (below). |
| 65 | |
| 66 | ## Stubs, and cross-plugin analysis |
| 67 | |
| 68 | PHPStan can't see WordPress, so `wordpress-stubs` supplies typed, implementation-free declarations of core functions/classes. The package is versioned to the WordPress version it was generated from. |
| 69 | |
| 70 | To analyze code that calls **another plugin's** API (e.g. WooCommerce), add that plugin's stubs and register them: |
| 71 | |
| 72 | ```bash |
| 73 | composer require --dev php-stubs/woocommerce-stubs |
| 74 | ``` |
| 75 | |
| 76 | ```neon |
| 77 | parameters: |
| 78 | bootstrapFiles: |
| 79 | - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php |
| 80 | ``` |
| 81 | |
| 82 | `php-stubs/woocommerce-stubs` depends on `wordpress-stubs`, so WP stubs come along. Without the right stubs, PHPStan reports "unknown function/class" for every external call. |
| 83 | |
| 84 | ## The baseline workflow (legacy code) |
| 85 | |
| 86 | Don't fight thousands of pre-existing errors. Snapshot them and only gate new/changed code: |
| 87 | |
| 88 | ```bash |
| 89 | vendor/bin/phpstan analyse --generate-baseline |
| 90 | ``` |
| 91 | |
| 92 | This writes `phpstan-baseline.neon` (every current error). Include it: |
| 93 | |
| 94 | ```neon |
| 95 | includes: |
| 96 | - phpstan-baseline.neon |
| 97 | ``` |
| 98 | |
| 99 | Now CI is green, but any **new** error fail |