PHPStan static analysis in Sage/Acorn projects — lando composer phpstan, phpstan.neon configuration, PHPStan level 0-9, WordPress PHP stubs, szepeviktor/phpstan-wordpress, baseline generation, CI phpstan, suppress errors, type errors, return type mismatch, nullable types, WordPress function stubs, WP_Post WP_Query WP_User types
Scanned 9/11/2026
Install to Claude Code
npx -y skills add hekivo/superpowers-sage --skill wp-phpstan --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Wp Phpstan?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/hekivo-wp-phpstan)More formats (shields.io, HTML) on the badges page.
---
name: superpowers-sage:wp-phpstan
description: >
PHPStan static analysis in Sage/Acorn projects — lando composer phpstan,
phpstan.neon configuration, PHPStan level 0-9, WordPress PHP stubs,
szepeviktor/phpstan-wordpress, baseline generation, CI phpstan,
suppress errors, type errors, return type mismatch, nullable types,
WordPress function stubs, WP_Post WP_Query WP_User types
user-invocable: false
---
# Static Analysis for Sage/Acorn with PHPStan
## When to use
- Setting up static analysis on a Sage theme for the first time
- Adding PHPStan to an existing project and managing the baseline
- Fixing type errors reported by PHPStan in Sage/Acorn code
- Configuring CI pipelines to enforce static analysis
- Incrementally raising the PHPStan analysis level
## Inputs required
- Current PHPStan level (if already configured), or confirmation this is a fresh setup
- Whether Larastan and WordPress stubs are already installed
- Target analysis level (or follow the recommended progression)
- CI platform in use (GitHub Actions, GitLab CI, etc.) if configuring CI integration
## Procedure
### 1. Install dependencies
```bash
lando theme-composer require --dev phpstan/phpstan larastan/larastan szepeviktor/phpstan-wordpress
```
This installs:
- **phpstan/phpstan** — the core static analysis engine
- **larastan/larastan** — Laravel-specific extensions (facades, models, service container, Eloquent)
- **szepeviktor/phpstan-wordpress** — WordPress function stubs and type definitions
### 2. Configure `phpstan.neon`
Create or update `phpstan.neon` in the theme root:
```neon
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
```
### 3. Run the initial analysis
```bash
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.
### 4. Generate a baseline
A baseline captures all existing errors so they can be addressed incrementally without blocking development.
```bash
lando theme-composer exec phpstan -- analyse --generate-baseline
```
This creates `phpstan-baseline.neon`. Include it in the main config:
```neon
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.
### 5. Common fixes for Sage/Acorn/WordPress patterns
#### WordPress hook callbacks
```php
// 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>';
});
```
#### ACF field return types
```php
// 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') ?: [];
}
```
#### Mixed return from `get_field`
```php
// 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'];
}
```
#### Eloquent model properties
Larastan handles most Eloquent patterns automatically. For custom model attributes, add PHPDoc:
```php
/**
* @property int $id
* @property string $title
* @property \Carbon\Carbon $created_at
*/
class Event extends Model
{
// ...
}
```
### 6. Level progression strategy
| 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:**
- **0 to 3**: Move quickly. These levels catch real bugs with minimal effort. Fix issues as you go.
- **3 to 5**: Requires adding return types and parameter types. Use the baseline to manage existing code while enforcing types on new code.
- **5+**: Only pursue if the team commits to strict typing throughout the codebase.
After raising the level, regenerate the baseline:
```bash
lando theme-composer exec phpstan -- analyse --generate-baseline
```
### 7. CI integration
Run PHPStan in CI without progress output:
```bash
lando theme-composer exec phpstan -- analyse --no-progress --error-format=github
```
#### GitHub Actions example
```yaml
- 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.
#### Preventing baseline growth
In CI, verify the baseline has not grown:
```bash
# 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.
### 8. Integration with Acorn
Larastan provides analysis support for Laravel patterns used in Acorn:
- **Facades**: Larastan resolves facade accessors to their underlying classes
- **Service container**: `app()->make()` and dependency injection are type-aware
- **Eloquent models**: relationships, scopes, and attributes are analyzed
- **Collections**: return types through collection chains are tracked
- **Config/View**: `config()` and `view()` calls are understood
No additional configuration is needed beyond including `larastan/extension.neon`.
## Verification
- [ ] `lando theme-composer exec phpstan -- analyse` runs without errors (or only baseline errors)
- [ ] `phpstan.neon` includes both Larastan and WordPress stubs extensions
- [ ] Baseline file exists and is included in the config
- [ ] Analysis level matches the project's target level
- [ ] CI pipeline runs PHPStan and fails on new errors
- [ ] No new errors added to the baseline without justification
## Failure modes
| 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` |
## Escalation
- If PHPStan reports errors that appear to be false positives in WordPress or Acorn code, check the GitHub issues for `szepeviktor/phpstan-wordpress` and `larastan/larastan` before suppressing
- For complex type issues involving WordPress hooks or ACF, consider writing custom PHPStan extensions or type stubs
- If the baseline grows uncontrollably, schedule a dedicated refactoring session to reduce it rather than ignoring it
- For questions about level progression strategy, discuss with the team lead to set a realistic target level
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!