Skip to content

Instantly share code, notes, and snippets.

@westonruter
Created April 18, 2026 03:09
Show Gist options
  • Select an option

  • Save westonruter/290276aa26efeb43a5029edc99309dc1 to your computer and use it in GitHub Desktop.

Select an option

Save westonruter/290276aa26efeb43a5029edc99309dc1 to your computer and use it in GitHub Desktop.

PHPStan performance in wordpress-develop: avoid pointing scanDirectories at directories containing vendor/ or node_modules/

Symptom

composer phpstan slow; PhpStorm's on-the-fly PHPStan inspection lags badly (many seconds per save).

Root cause

PHPStan walks every .php file under each entry in scanDirectories to build its symbol table. If an entry contains vendor/ or node_modules/, every third-party file gets parsed on every run.

excludePaths: analyseAndScan does not meaningfully help here: excludes are applied after enumeration, so the traversal and parsing cost is mostly still paid. In testing, enabling excludes only saved ~2s out of a 34s warm run.

Fix

Point scanDirectories at the source subdirectories of the plugins/tools you need symbols from — never at a directory that contains vendor/ or node_modules/. Use scanFiles for individual entry-point files at the package root.

Example from phpstan.neon:

# Bad — pulls in ~9000 third-party files
scanDirectories:
    - src/wp-content/plugins/performance
    - src/wp-content/plugins/ai

# Good — only source dirs
scanDirectories:
    - src/wp-content/plugins/performance/plugins
    - src/wp-content/plugins/ai/includes
    - src/wp-content/plugins/ai/src
    - src/wp-content/plugins/ai/routes

scanFiles:
    - src/wp-content/plugins/performance/performance.php
    - src/wp-content/plugins/ai/ai.php

Measured impact on wordpress-develop (level 10, 40,078 errors, unchanged)

Scenario Before After
Cold full run 86s 33s
Warm full run 34s 8s
Single-file (PhpStorm --tmp-file/--instead-of) 8–10s ~1s

Diagnostic recipe

  1. Warm the cache: composer phpstan once.

  2. Time a single-file editor-mode run, which is what PhpStorm does per inspection pass:

    FILE=src/wp-includes/functions.php
    time ./vendor/bin/phpstan analyse --memory-limit=2G \
        --error-format=json --tmp-file=$FILE --instead-of=$FILE > /dev/null

    If this is >2s, something in config is over-scanning.

  3. Compare against --configuration=phpstan.neon.dist (no local scanDirectories). If that one is fast and your config is slow, the difference is in your scanDirectories / paths.

  4. For any directory you're scanning, check for vendor/ or node_modules/ subtrees:

    find <scanDir>/vendor <scanDir>/node_modules -name '*.php' 2>/dev/null | wc -l

Related things that didn't matter much

  • level (0 vs 10): negligible difference on single-file timing.
  • --memory-limit: not the bottleneck at 2G.
  • parallel.* tuning: irrelevant for single-file editor mode (runs in one process anyway).
  • Result cache size (tens of MB is fine): loading it is cheap compared to scanning thousands of extra files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment