In a phpstan.neon file, what do level, paths, excludePaths and includes do, and how do command-line arguments interact with them?
answer
- NEON, close to YAML; parameters: block
- phpstan.neon, then .neon.dist, then .dist.neon
- CLI paths replace config paths
- excludePaths: fnmatch; analyse vs analyseAndScan
- includes: compose a local override
basics
~20 sUnder parameters, level sets the rule level, paths lists what to analyse and excludePaths removes fnmatch patterns; includes pulls in other config files. A --level or paths given on the command line replace the config values rather than merging with them.
solid answer
~40 s`phpstan.neon` is written in NEON, a YAML-like format. Under `parameters:`, `level` sets the rule level and `paths` lists the directories to analyse, so plain `vendor/bin/phpstan` works. `excludePaths` takes `fnmatch()` patterns: a flat list means `analyseAndScan`, excluded entirely, while `excludePaths: analyse:` skips reporting on the files but still reads their symbols, which suits bundled third-party code. `includes:` composes files, for example a git-ignored `phpstan.neon` that includes the committed `phpstan.neon.dist` and overrides a few settings, or an extension's `extension.neon`. PHPStan picks the config from `-c`, else `phpstan.neon`, else `phpstan.neon.dist`, else `phpstan.dist.neon`. Paths given on the command line are not merged with `paths`; only the CLI list is used. `--level` likewise overrides `level`. PHPStan 2 removed the old `excludes_analyse` key in favour of `excludePaths`.
code
yaml · 7 lines# phpstan.neon (git-ignored local override)
includes:
- phpstan.neon.dist
parameters:
parallel:
maximumNumberOfProcesses: 1go deeper
Know that phpstan.neon holds level and paths under parameters, so vendor/bin/phpstan runs without arguments.
Explain excludePaths with analyse versus analyseAndScan, includes for local overrides, file discovery order, and that CLI paths and --level replace config values.
Structure configs for a team: a committed dist file, a local override, extensions and Bleeding Edge via includes, tmpDir inside the workspace for CI caching.
Standardise configuration across repositories, for example a shared included base file, without hiding per-project exceptions from review.
## The file and how PHPStan finds it PHPStan's configuration is written in **NEON**, a format very close to YAML (tab or space indentation, `-` for list items). Almost everything lives under a `parameters:` key. When you do not pass `-c`, PHPStan looks in the current directory in this order: 1. the file given with `-c` / `--configuration`; 2. `phpstan.neon`; 3. `phpstan.neon.dist`; 4. `phpstan.dist.neon`; 5. otherwise, no config at all. The usual convention is to commit `phpstan.neon.dist` (or `phpstan.dist.neon`) and let each developer or CI machine create a git-ignored `phpstan.neon` that includes it and overrides a few settings. ## The four keys | Key | What it does | Notes | |---|---|---| | `level` | the rule level, 0 to 10 or `max` | `--level` on the CLI takes precedence | | `paths` | files and directories to analyse | paths on the CLI replace this list, they are not merged | | `excludePaths` | `fnmatch()` patterns to leave out | plain list = `analyseAndScan`; `analyse:` keeps symbols visible | | `includes` | other config files to merge in (top-level key, not under `parameters`) | relative paths resolve against the including file's directory | Relative paths in `paths`, `excludePaths`, `includes` and `tmpDir` are resolved against the **directory of the config file**, not the directory you run the command from. Paths passed on the command line resolve against the current working directory. ## excludePaths in detail - A flat list is shorthand for `excludePaths: analyseAndScan:`: the files are neither analysed nor read for symbols. Use it for files that are broken on purpose, such as test fixtures with invalid PHP. - `excludePaths: analyse:` stops PHPStan reporting errors in those files but still lets it **discover the classes and functions they declare**. That is the right choice for a bundled `src/thirdparty` directory your code calls into. - In PHPStan 2 each entry must be an existing path or a valid `fnmatch` pattern; appending `(?)` marks an entry that may not exist. The old `excludes_analyse` key from 1.x was removed. - Do not put `vendor/` in `paths` to begin with. PHPStan discovers Composer dependencies' symbols automatically; analysing them only reports bugs you cannot fix. ## includes in detail `includes` merges other NEON (or PHP-returned) config files into the current one. Common uses: - A **local override**: `phpstan.neon` with `includes: - phpstan.neon.dist` plus, say, `parallel: maximumNumberOfProcesses: 1` on a small laptop. - **Bleeding Edge**: `includes: - phar://phpstan.phar/conf/bleedingEdge.neon`. - **Extensions** without the extension installer: `includes: - vendor/phpstan/phpstan-strict-rules/rules.neon`. - A generated **baseline** file, which is its own topic. If the same file ends up included twice, for example once by hand and once by `phpstan/extension-installer`, PHPStan refuses to start with a *files are included multiple times* error and suggests removing the manual include. ## How the command line interacts 1. `vendor/bin/phpstan analyse --level 7` runs level 7 whatever the config says. 2. `vendor/bin/phpstan analyse src/Billing` analyses only `src/Billing`, ignoring the config's `paths` list entirely. 3. With a config file present, one of `--level` or `level:` is mandatory; the default level 0 only applies when there is no config. 4. Running with different path lists from run to run also defeats the result cache, which is keyed on the analysed paths. ## Other keys you meet early A few more `parameters` keys come up in almost every real configuration: - `fileExtensions`: PHPStan analyses only `.php` files by default; legacy code with `.inc` or `.module` files needs them listed. - `scanFiles` and `scanDirectories`: code PHPStan should read for symbols but not analyse, such as classes outside Composer's autoloader. - `bootstrapFiles`: PHP files PHPStan **executes** before analysis, for example to register a custom autoloader or define class aliases. - `customRulesetUsed: true`: tells PHPStan not to require a level because you assemble your own rules from the level config files. - `parallel`: tuning for the worker processes, such as `maximumNumberOfProcesses`. ## A complete example ```yaml includes: - phar://phpstan.phar/conf/bleedingEdge.neon parameters: level: 6 paths: - src - tests excludePaths: analyse: - src/Legacy/ThirdParty analyseAndScan: - tests/*/Fixtures/* tmpDir: var/phpstan ``` With this file committed as `phpstan.dist.neon`, `vendor/bin/phpstan` with no arguments analyses `src` and `tests` at level 6 with Bleeding Edge, and keeps its cache in `var/phpstan`.
- What is the difference between excludePaths: analyse and excludePaths: analyseAndScan?`analyse` stops PHPStan reporting errors in those files but still reads them to discover the classes and functions they declare. `analyseAndScan`, which a flat list defaults to, removes them completely, so code that uses their symbols may then report unknown classes.
- Your config lists src and tests, but CI runs vendor/bin/phpstan analyse src; what is analysed?Only `src`. Paths passed on the command line replace the `paths` parameter instead of merging with it, so `tests` is skipped. It also changes the result-cache key, because the cache depends on the analysed paths.
saying these in an interview costs you the question
- Paths on the command line are added to the paths in phpstan.neon
- The vendor directory should be listed in paths so PHPStan knows the classes
- excludes_analyse is the current key for excluding files
- Relative paths in phpstan.neon resolve from the current working directory
- The level in phpstan.neon always wins over --level