WordPress and static analysis have a complicated relationship. The WordPress codebase predates modern PHP by a decade, and PHPStan doesn't understand $wpdb->get_results() returns mixed, not a typed array.
Here's how I got PHPStan level 8 running on every plugin at Codexpert.
The Problem
Out of the box, PHPStan will explode on any WordPress plugin:
Function apply_filters() not found.
Access to an undefined property WP_Post::$post_type.
WordPress functions aren't in any autoloadable namespace β they're global functions defined at runtime.
Step 1: WordPress Stubs
Install php-stubs/wordpress-stubs:
composer require --dev php-stubs/wordpress-stubs
Add to phpstan.neon:
parameters:
bootstrapFiles:
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
stubFiles:
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
Step 2: WooCommerce Stubs
If your plugin uses WooCommerce:
composer require --dev php-stubs/woocommerce-stubs
For Dokan we maintain our own internal stubs β the official WC stubs don't cover every filter and action. We ship phpstan-dokan-stubs as an open-source package.
Step 3: Ignore What You Can't Fix
Some WordPress patterns can't be statically typed:
parameters:
ignoreErrors:
- '#Call to an undefined method WP_Error\:\:#'
- '#Unsafe usage of new static\(\)#'
message: '#Variable \$wpdb might not be defined#'
path: src/
Step 4: Incremental Level Increases
Don't start at level 8. Start at level 1 and fix errors before going to level 2. With a 10k-line plugin you'll have ~50 errors at level 1. That's manageable. Level 8 will show 500 and you'll give up.
We run phpstan analyse --level=max in CI and treat any new error as a build failure. Existing errors live in a baseline file.
# Generate baseline for existing code
phpstan analyse --generate-baseline phpstan-baseline.neon
Results
After six months of incremental adoption across our plugin suite:
- Caught 12 real bugs that would have reached production
- Eliminated an entire category of null-dereference crashes
- Onboarding new engineers is faster because the type signatures document intent
Al Amin Ahamed
Senior software engineer & AI practitioner. Building things in Laravel, PHP, and TypeScript.
About me ββ Older
Building a WooCommerce Payment Gateway from Scratch
Newer β
Laravel Queues at Scale: Lessons from 10 Million Jobs
One email a month. No noise.
What I shipped, what I read, occasional deep dive. Unsubscribe anytime.