Skip to main content
Version: v1

PHPStan - PHP static analysis tool

The PHPStan project describes the tool as follows:

PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code can be checked before you run the actual line.

PHPStan checks every code path, including the paths your tests never reach. It reports problems such as a call to a method that doesn't exist, an argument of the wrong type or a missing return type. Vortex comes with a pre-configured PHPStan ruleset for Drupal projects, so the analysis runs on every build without setup.

This page is for developers who write PHP in a Vortex project. It shows how to run PHPStan, what the shipped configuration analyzes, how to ignore an error you can't fix and how to onboard an existing codebase with a baseline. For PHPStan itself, see the PHPStan user guide.

Usage​

The Ahoy command runs PHPCS, PHPStan and Rector in that order. The Docker Compose command runs PHPStan alone. To analyze the code, run 1 of the following commands:

ahoy lint-be

Confirm that the analysis passed: PHPStan prints [OK] No errors.

note

PHPStan only reports errors and leaves your code unchanged. Rector fixes some of the same problems automatically, such as missing type declarations.

➡️ See Rector

Configuration​

See the PHPStan configuration reference.

All global configuration lives in the phpstan.neon file.

The analysis runs at level 7, with the Drupal-specific rules from mglaman/phpstan-drupal, which phpstan/extension-installer loads automatically.

The phpVersion value in phpstan.neon, set to 80427, makes PHPStan treat the code as PHP 8.4.27, the version the composer.json key config.platform.php pins. PHPStan doesn't read that key, so update both when you move to a new PHP version.

treatPhpDocTypesAsCertain is set to false because the types in doc comments go out of date, so PHPStan relies on native type declarations.

The ignoreErrors list silences 2 errors that Drupal code can't avoid:

  • Missing iterable value types in custom modules, custom themes and tests/phpunit
  • Possibly undefined variables in the included settings files

PHPStan analyzes the following paths, which the paths list in phpstan.neon sets:

  • web/modules/custom
  • web/themes/custom
  • web/sites/default/settings.php
  • web/sites/default/includes
  • tests
  • scripts

To add or remove targets, edit the paths and excludePaths lists in phpstan.neon:

parameters:
paths:
- path/to/dir_or_file
excludePaths:
- path/to/exclude/all_dir_files/*
- path/to/exclude/a_file.php

Ignore violations​

To ignore an error across the project, add it to ignoreErrors in the phpstan.neon file:

parameters:
ignoreErrors:
- # Comment about why this rule is excluded.
# 'message' is a regular expression with `#` as begin and end delimiters.
message: '#.*no value type specified in iterable type array.#'
paths:
- path/to/exclude/all_dir_files/*
- path/to/exclude/a_file.php

PHPStan's inline comments work 1 line at a time. To ignore errors in whole files, use the configuration:

  • To leave files out of the analysis, add them to excludePaths.
  • To silence 1 error in some files only, add paths to its ignoreErrors entry, as in the preceding example.

The PHPStan issue Ignoring code blocks using PHPDocs explains why PHPStan has no block-level ignore.

To ignore 1 error on the next line, add a @phpstan-ignore comment with the error identifier that PHPStan prints with the error:

// @phpstan-ignore variable.undefined
echo $title;

To ignore an error on the same line instead, add the comment at the end of that line.

To ignore every error on the next line, whatever its identifier, add this comment on the line before it:

// @phpstan-ignore-next-line
$a = 1;

Baseline for an existing codebase​

When you install Vortex into an existing codebase, the first analysis reports every error that the code already has. A baseline records those errors in phpstan-baseline.neon, and PHPStan then reports only the errors that the baseline doesn't record. Every new error fails the analysis, and you fix the recorded errors over time.

Use the baseline only to onboard existing code. To ignore a new error, see Ignore violations.

phpstan.neon ships with the include of phpstan-baseline.neon commented out, so PHPStan uses the baseline only after you uncomment it.

Generate the baseline​

To record the current errors in phpstan-baseline.neon, run 1 of the following commands:

ahoy lint-be-baseline

PHPStan prints [OK] Baseline generated with and the number of errors it recorded.

To load the baseline, uncomment the includes lines in phpstan.neon:

includes:
- phpstan-baseline.neon

Commit phpstan-baseline.neon and phpstan.neon. CI reads the committed baseline and never generates one, so a new error can't enter the baseline unnoticed.

Run ahoy cli vendor/bin/phpstan and confirm that PHPStan prints [OK] No errors. ahoy lint-be runs PHPCS first and stops on its errors, so it can end before PHPStan runs.

Shrink the baseline​

After you fix a recorded error, PHPStan fails with Ignored error pattern and was not matched in reported errors, because the baseline still records the error. To remove the fixed errors from the baseline, run ahoy lint-be-baseline again and commit the updated file.

Regenerate the baseline only after you fix errors. A regeneration records every error in the code, including a new one you meant to fix.

When the baseline records no errors, delete phpstan-baseline.neon and comment out the includes lines in phpstan.neon again. PHPStan stops with an error when an included file is missing.

Continuous integration​

PHPStan runs in the lint job of the continuous integration pipeline and fails the build on violations.

Ignore failures​

To let the build pass anyway, set the VORTEX_CI_PHPSTAN_IGNORE_FAILURE CI variable to 1. PHPStan still runs and prints every error in the job log, so you can see what you're letting through.

➡️ See Continuous integration > Ignore tool failures