PHPStan level 8 enforces the strictest type analysis — it reports missing return types, mixed type propagation, and unsafe array access that lower levels ignore. Getting a WordPress plugin to pass level 8 requires proper type annotations and understanding how PHPStan handles WordPress’s dynamically-typed functions.
Problem: A WordPress plugin codebase has accumulated years of loosely typed PHP — mixed return types, nullable parameters without declarations, and dynamic property access — making it difficult to adopt PHPStan at any level above 0 without thousands of errors.
Solution: Use PHPStan's baseline feature: run vendor/bin/phpstan analyse --generate-baseline to record all existing errors into phpstan-baseline.neon, then commit the file. The CI pipeline passes immediately, and new code must meet the configured level. Shrink the baseline over time by fixing one category of errors per sprint.
The examples below configure PHPStan level 8 with the WordPress stubs, show the most common level 8 errors and their fixes, and set up a baseline file to suppress legacy violations while enforcing strict types on new code.
# Install PHPStan + WordPress stubs
composer require --dev phpstan/phpstan
composer require --dev szepeviktor/phpstan-wordpress # WP function return types
composer require --dev php-stubs/wordpress-stubs # WP globals and constants
PHPStan configuration file:
# phpstan.neon
parameters:
level: 8
paths:
- src
- includes
excludePaths:
- vendor
- node_modules
bootstrapFiles:
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
extensions:
- szepeviktor/phpstan-wordpress
# Treat 'mixed' type as an error (required for level 8 strictness)
checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: false
# Ignore WP functions that legitimately return mixed
ignoreErrors:
- '#Call to function get_option\(\) with#'
post_title; // PHPStan: Cannot access property on WP_Post|null
// GOOD: narrow the type
$post = get_post( $id );
if ( ! $post instanceof WP_Post ) return;
echo $post->post_title;
// ERROR: mixed type in array — array not allowed
// BAD:
function get_config(): array {
return get_option( 'my_settings', [] ); // returns mixed
}
// GOOD: cast and type-narrow
function get_config(): array {
$raw = get_option( 'my_settings', [] );
return is_array( $raw ) ? $raw : [];
}
// ERROR: missing return type on a method
// BAD:
public function process( $input ) {
return strtoupper( $input );
}
// GOOD:
public function process( string $input ): string {
return strtoupper( $input );
}
# Generate a baseline to suppress existing errors (fix incrementally)
vendor/bin/phpstan analyse --generate-baseline phpstan-baseline.neon
# Add to phpstan.neon:
# includes:
# - phpstan-baseline.neon
# Now run clean — only NEW code must pass level 8
vendor/bin/phpstan analyse
# CI step — fail on any new violations
# vendor/bin/phpstan analyse --error-format=github
NOTE: The baseline approach lets you adopt PHPStan level 8 on an existing codebase without fixing thousands of existing errors at once — new files and modified functions must pass level 8, while the baseline slowly shrinks as legacy code is refactored.