From superpowers-sage
Sets up PHPStan static analysis for Sage/Acorn WordPress projects with Larastan, WordPress stubs, phpstan.neon config, baseline generation, CI integration, and error fixes.
npx claudepluginhub codigodoleo/superpowers-sage --plugin superpowers-sageThis skill uses the workspace's default tool permissions.
- Setting up static analysis on a Sage theme for the first time
Configures, runs, and fixes PHPStan static analysis in WordPress projects: phpstan.neon setup, baselines, WordPress-specific typing, third-party plugin classes.
Reviews WordPress PHP/JS/CSS/HTML code against WPCS standards, static analysis with PHPCS/PHPStan/ESLint, architecture patterns, error handling, and deprecated functions. Use for custom themes/plugins.
Guides PHPStan error resolution prioritizing refactoring over phpDoc and ignoring, with Nette patterns, baseline management, and type tests. Use before running PHPStan or fixing errors.
Share bugs, ideas, or general feedback.
lando theme-composer require --dev phpstan/phpstan larastan/larastan szepeviktor/phpstan-wordpress
This installs:
phpstan.neonCreate or update phpstan.neon in the theme root:
includes:
- vendor/larastan/larastan/extension.neon
- vendor/szepeviktor/phpstan-wordpress/extension.neon
parameters:
paths:
- app/
level: 0
# WordPress dynamic functions that PHPStan cannot resolve
ignoreErrors:
- '#Function apply_filters invoked with#'
- '#Function do_action invoked with#'
- '#Function add_filter expects#'
- '#Function add_action expects#'
# Scan files for WordPress global function definitions
scanDirectories:
- vendor/szepeviktor/phpstan-wordpress/bootstrap.php
# Treat Acorn facades correctly via Larastan
checkGenericClassInNonGenericObjectType: false
lando theme-composer exec phpstan -- analyse
At level 0, this should produce few or no errors. If errors exist, fix them or generate a baseline.
A baseline captures all existing errors so they can be addressed incrementally without blocking development.
lando theme-composer exec phpstan -- analyse --generate-baseline
This creates phpstan-baseline.neon. Include it in the main config:
includes:
- vendor/larastan/larastan/extension.neon
- vendor/szepeviktor/phpstan-wordpress/extension.neon
- phpstan-baseline.neon
New code must pass analysis; baseline errors are addressed over time.
// PHPStan complains about callable types
// Fix: use explicit Closure or typed callable
add_filter('the_content', function (string $content): string {
return $content . '<p>Appended</p>';
});
// get_field() returns mixed — PHPStan cannot infer the type
// Fix: assert or cast the return value
/** @var string|null $subtitle */
$subtitle = get_field('subtitle');
// Or use a wrapper with PHPDoc
/**
* @return array<string, mixed>
*/
function get_hero_fields(): array
{
return (array) get_field('hero_section') ?: [];
}
get_field// Bad — PHPStan reports "Cannot access property on mixed"
$image = get_field('image');
echo $image['url']; // Error
// Good — type-narrow first
$image = get_field('image');
if (is_array($image) && isset($image['url'])) {
echo $image['url'];
}
Larastan handles most Eloquent patterns automatically. For custom model attributes, add PHPDoc:
/**
* @property int $id
* @property string $title
* @property \Carbon\Carbon $created_at
*/
class Event extends Model
{
// ...
}
| Level | Catches | Effort | Recommendation |
|---|---|---|---|
| 0 | Basic errors, unknown classes | Minimal | Start here |
| 1 | Possibly undefined variables | Low | Move to quickly |
| 2 | Unknown methods on mixed | Low | Move to quickly |
| 3 | Return types checked | Moderate | First real milestone |
| 4 | Basic dead code detection | Moderate | Good target for most projects |
| 5 | Argument types checked | High | Requires disciplined typing |
| 6+ | Strict union/intersection types | Very high | For projects committed to strict typing |
Recommended progression:
After raising the level, regenerate the baseline:
lando theme-composer exec phpstan -- analyse --generate-baseline
Run PHPStan in CI without progress output:
lando theme-composer exec phpstan -- analyse --no-progress --error-format=github
- name: Run PHPStan
run: |
cd web/app/themes/<theme-name>
composer exec phpstan -- analyse --no-progress --error-format=github
The --error-format=github flag produces annotations that appear inline on pull requests.
In CI, verify the baseline has not grown:
# Regenerate baseline and check for changes
lando theme-composer exec phpstan -- analyse --generate-baseline
git diff --exit-code phpstan-baseline.neon
If the diff is non-empty, the PR introduces new errors that must be fixed.
Larastan provides analysis support for Laravel patterns used in Acorn:
app()->make() and dependency injection are type-awareconfig() and view() calls are understoodNo additional configuration is needed beyond including larastan/extension.neon.
lando theme-composer exec phpstan -- analyse runs without errors (or only baseline errors)phpstan.neon includes both Larastan and WordPress stubs extensions| Symptom | Likely cause | Fix |
|---|---|---|
| "Class not found" for WordPress functions | WordPress stubs not installed or not included | Verify szepeviktor/phpstan-wordpress is installed and included in phpstan.neon |
| "Class not found" for Acorn facades | Larastan not included | Verify larastan/larastan is installed and extension.neon is included |
| Hundreds of errors on first run | Level set too high for current codebase | Start at level 0, generate baseline, increment gradually |
| Baseline keeps growing | New code introducing type errors | Enforce CI check that baseline does not grow |
| Memory exhaustion during analysis | Large codebase or too many paths scanned | Limit paths to app/, increase PHP memory limit in phpstan.neon: parameters.memoryLimit: 512M |
| False positives on WordPress hooks | Dynamic invocation patterns | Add specific patterns to ignoreErrors in phpstan.neon |
| Larastan conflicts with PHPStan version | Version mismatch | Check Larastan compatibility matrix, pin compatible versions in composer.json |
szepeviktor/phpstan-wordpress and larastan/larastan before suppressing