Back to skills
SkillHub ClubShip Full StackFull StackIntegration

wp-phpstan

Use when configuring, running, or fixing PHPStan static analysis in WordPress projects (plugins/themes/sites): phpstan.neon setup, baselines, WordPress-specific typing, and handling third-party plugin classes.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
923
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
A88.4

Install command

npx @skill-hub/cli install wordpress-agent-skills-wp-phpstan

Repository

WordPress/agent-skills

Skill path: skills/wp-phpstan

Use when configuring, running, or fixing PHPStan static analysis in WordPress projects (plugins/themes/sites): phpstan.neon setup, baselines, WordPress-specific typing, and handling third-party plugin classes.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Integration.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: WordPress.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install wp-phpstan into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/WordPress/agent-skills before adding wp-phpstan to shared team environments
  • Use wp-phpstan for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: wp-phpstan
description: "Use when configuring, running, or fixing PHPStan static analysis in WordPress projects (plugins/themes/sites): phpstan.neon setup, baselines, WordPress-specific typing, and handling third-party plugin classes."
compatibility: "Targets WordPress 6.9+ (PHP 7.2.24+). Requires Composer-based PHPStan."
---

# WP PHPStan

## When to use

Use this skill when working on PHPStan in a WordPress codebase, for example:

- setting up or updating `phpstan.neon` / `phpstan.neon.dist`
- generating or updating `phpstan-baseline.neon`
- fixing PHPStan errors via WordPress-friendly PHPDoc (REST requests, hooks, query results)
- handling third-party plugin/theme classes safely (stubs/autoload/targeted ignores)

## Inputs required

- `wp-project-triage` output (run first if you haven't)
- Whether adding/updating Composer dev dependencies is allowed (stubs).
- Whether changing the baseline is allowed for this task.

## Procedure

### 0) Discover PHPStan entrypoints (deterministic)
1. Inspect PHPStan setup (config, baseline, scripts):
   - `node skills/wp-phpstan/scripts/phpstan_inspect.mjs`

Prefer the repo’s existing `composer` script (e.g. `composer run phpstan`) when present.

### 1) Ensure WordPress core stubs are loaded

`szepeviktor/phpstan-wordpress` or `php-stubs/wordpress-stubs` are effectively required for most WordPress plugin/theme repos. Without it, expect a high volume of errors about unknown WordPress core functions.

- Confirm the package is installed (see `composer.dependencies` in the inspect report).
- Ensure the PHPStan config references the stubs (see `references/third-party-classes.md`).

### 2) Ensure a sane `phpstan.neon` for WordPress projects

- Keep `paths` focused on first-party code (plugin/theme directories).
- Exclude generated and vendored code (`vendor/`, `node_modules/`, build artifacts, tests unless explicitly analyzed).
- Keep `ignoreErrors` entries narrow and documented.

See:
- `references/configuration.md`

### 3) Fix errors with WordPress-specific typing (preferred)

Prefer correcting types over ignoring errors. Common WP patterns that need help:

- REST endpoints: type request parameters using `WP_REST_Request<...>`
- Hook callbacks: add accurate `@param` types for callback args
- Database results and iterables: use array shapes or object shapes for query results
- Action Scheduler: type `$args` array shapes for job callbacks

See:
- `references/wordpress-annotations.md`

### 4) Handle third-party plugin/theme classes (only when needed)

When integrating with plugins/themes not present in the analysis environment:

- First, confirm the dependency is real (installed/required).
- Prefer plugin-specific stubs already used in the repo (common examples: `php-stubs/woocommerce-stubs`, `php-stubs/acf-pro-stubs`).
- If PHPStan still cannot resolve classes, add targeted `ignoreErrors` patterns for the specific vendor prefix.

See:
- `references/third-party-classes.md`

### 5) Baseline management (use as a migration tool, not a trash bin)

- Generate a baseline once for legacy code, then reduce it over time.
- Do not “baseline” newly introduced errors.

See:
- `references/configuration.md`

## Verification

- Run PHPStan using the discovered command (`composer run ...` or `vendor/bin/phpstan analyse`).
- Confirm the baseline file (if used) is included and didn’t grow unexpectedly.
- Re-run after changing `ignoreErrors` to ensure patterns are not masking unrelated issues.

## Failure modes / debugging

- “Class not found”:
  - confirm autoloading/stubs, or add a narrow ignore pattern
- Huge error counts after enabling PHPStan:
  - reduce `paths`, add `excludePaths`, start at a lower level, then ratchet up
- Inconsistent types around hooks / REST params:
  - add explicit PHPDoc (see references) rather than runtime guards

## Escalation

- If a type depends on a third-party plugin API you can’t confirm, ask for the dependency version or source before inventing types.
- If fixing requires adding new Composer dependencies (stubs/extensions), confirm it with the user first.


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/third-party-classes.md

```markdown
# Third-party classes and ignore patterns

When PHPStan reports legitimate classes as missing (e.g. because WordPress or a plugin is not installed in the analysis environment), prefer fixing discovery first and only then add targeted ignores.

## Before adding `ignoreErrors`

- Confirm the dependency is real (installed/required in this environment).
- Prefer stubs/extensions already used by the repo.
- Prefer a narrow ignore for the vendor prefix over a broad ignore.

## Recommended stub packages

Stubs are useful when the analysis environment does not include WordPress (or a plugin API) but you still want real type checking (instead of blanket ignores).

Common packages:

```bash
composer require --dev szepeviktor/phpstan-wordpress
composer require --dev php-stubs/wordpress-stubs
composer require --dev php-stubs/woocommerce-stubs
composer require --dev php-stubs/acf-pro-stubs
```

When stubs are useful (and sometimes necessary):

- Running PHPStan in a plugin/theme repo without a full WordPress checkout.
- PHPStan reports unknown WordPress core functions (e.g. `add_action()`, `get_option()`).
- Integrations with optional plugins (WooCommerce, ACF Pro) that are not installed during analysis.
- You want method/property existence checks and accurate return types instead of `ignoreErrors`.

Notes:

- Prefer stubs that match the runtime versions; mismatches can cause false positives.
- Adding Composer dependencies changes the repo; confirm it is acceptable for the task.

## Ensure stubs are loaded

Installing stubs is not enough if PHPStan does not scan them. Add stub paths in `phpstan.neon`.

```neon
parameters:
    bootstrapFiles:
        - %rootDir%/../../php-stubs/woocommerce-stubs/woocommerce-stubs.php
    scanFiles:
        - %rootDir%/../../php-stubs/wordpress-stubs/wordpress-stubs.php
        - %rootDir%/../../php-stubs/acf-pro-stubs/acf-pro-stubs.php
        - %rootDir%/../../woocommerce/action-scheduler/functions.php
```

## Targeted ignore patterns (examples)

```neon
parameters:
    ignoreErrors:
        # Admin Columns Pro
        - '#.*(unknown class|invalid type|call to method .* on an unknown class) AC\\ListScreen.*#'

        # Elementor
        - '#.*(unknown class|invalid type|call to method .* on an unknown class) Elementor\\.*#'

        # Yoast SEO
        - '#.*(unknown class|invalid type|call to method .* on an unknown class) WPSEO_.*#'
```

Pattern creation rules:

- Cover error variations: `unknown class`, `invalid type`, `call to method .* on an unknown class`.
- Keep patterns specific enough to target only intended classes.
- Add a short comment naming the plugin/theme.
- Group related patterns for the same dependency.

When to add exceptions:

- Only for legitimate third-party dependencies your code integrates with.
- Document each pattern with a comment.
- Re-run PHPStan to ensure the ignore does not hide unrelated issues.

```

### references/configuration.md

```markdown
# PHPStan configuration (WordPress)

This reference documents a minimal, WordPress-friendly PHPStan setup and baseline workflow.

## Minimal `phpstan.neon` template

Use the repo’s existing layout. The example below is intentionally conservative and should be adapted to the project’s actual directories.

```neon
# Include the baseline only if the file exists.
includes:
    - phpstan-baseline.neon

parameters:
    level: 5
    paths:
        - src/
        - includes/

    excludePaths:
        - vendor/
        - vendor-prefixed/
        - node_modules/
        - tests/

    ignoreErrors:
        # Add targeted exceptions only when necessary.
```

Guidelines:

- Prefer analyzing first-party code only.
- Exclude anything generated or vendored.
- Keep `ignoreErrors` patterns narrow and grouped by dependency.

## Baseline workflow

Baselines help you adopt PHPStan in legacy code without accepting new regressions.

```bash
# Generate a baseline (explicit filename)
vendor/bin/phpstan analyse --generate-baseline phpstan-baseline.neon

# Update an existing baseline (defaults)
vendor/bin/phpstan analyse --generate-baseline
```

Best practices:

- Avoid adding new errors to the baseline; fix the new code instead.
- Treat baseline changes like code changes: review in PRs.
- Chip away at the baseline gradually (remove entries as you fix root causes).

```

### references/wordpress-annotations.md

```markdown
# WordPress-specific type annotations

These patterns help PHPStan understand WordPress code where runtime behavior and dynamic typing make inference difficult.

## REST API request typing

PHPStan cannot infer valid request parameters from REST API schemas. Provide explicit type hints for request params.

```php
/**
 * Handle REST API request.
 *
 * @param WP_REST_Request $request Full details about the request.
 * @return WP_REST_Response|WP_Error Response object on success, error on failure.
 *
 * @phpstan-param WP_REST_Request<array{
 *     post?: int,
 *     orderby?: string,
 *     meta_key?: string,
 *     per_page?: int,
 *     status?: array<string>
 * }> $request
 */
public function get_items( $request ) {
    $post_id = $request->get_param( 'post' );
    // PHPStan now knows $post_id is int|null.
}
```

For complex schemas, define reusable types.

```php
/**
 * @phpstan-type PostRequestParams array{
 *     title?: string,
 *     content?: string,
 *     status?: 'publish'|'draft'|'private',
 *     meta?: array<string, mixed>
 * }
 *
 * @phpstan-param WP_REST_Request<PostRequestParams> $request
 */
```

## Hook callbacks

```php
/**
 * Handle status transitions.
 *
 * @param string $new_status
 * @param string $old_status
 * @param WP_Post $post
 */
function handle_transition( string $new_status, string $old_status, WP_Post $post ): void {
    // ...
}

add_action( 'transition_post_status', 'handle_transition', 10, 3 );
```

## Database and iterables

```php
/**
 * @return array<WP_Post> WP_Post objects.
 */
function get_custom_posts(): array {
    $posts = get_posts( [ 'post_type' => 'custom_type', 'numberposts' => -1 ] );
    return $posts;
}

/**
 * @return array<object{id: int, name: string}> Database results.
 */
function get_user_data(): array {
    global $wpdb;

    $results = $wpdb->get_results( "SELECT id, name FROM users", OBJECT );
    return $results ?: [];
}
```

## Hooks (`apply_filters()` and `do_action()`)

Docblocks for `apply_filters()` and `do_action()` are validated. The type of the first `@param` is definitive.

If a third party returns the wrong type for a filter, a PHPStan error is expected and does not require defensive code.

```php
/**
 * Allows hooking into formatting of the price.
 *
 * @param string $formatted The formatted price.
 * @param float  $price     The raw price.
 * @param string $locale    Locale to localize pricing display.
 * @param string $currency  Currency symbol.
 */
return apply_filters( 'autoscout_vehicle_price_formatted', $formatted, $price, $locale, $currency );
```

## Action Scheduler argument shapes

```php
/**
 * Process a scheduled email.
 *
 * @param array{user_id: int, email: string, data: array<string, mixed>} $args
 */
function process_scheduled_email( array $args ): void {
    $user_id = $args['user_id'];
    // ...
}

as_schedule_single_action(
    time() + 3600,
    'process_scheduled_email',
    [
        'user_id' => 123,
        'email' => '[email protected]',
        'data' => [ 'key' => 'value' ],
    ]
);
```

```

wp-phpstan | SkillHub