Practical guide

How to adopt PHPStan in an existing project

Stop adding new type errors immediately and reduce existing debt measurably in small steps.

25 minutes · Static analysis

In short

Zero errors in one day is not the first goal

PHPStan can analyze a legacy project incrementally. First fix broken bootstrap, unknown symbols, and errors in the area being changed, then lock any remainder in a baseline.

A baseline is not a trash bin. It lets CI block new problems at a strict rule level while the number of legacy exceptions deliberately decreases.

Prepare

Map the project’s real runtime

Analysis must know the same PHP, framework types, and generated code as the application.

  • The supported PHP version and current composer.lock running locally and in CI.
  • Directories containing your code and tests; vendor and cache are not analyzed directly.
  • A framework extension or bootstrapFiles only where symbols are created dynamically.
  • An owner for reviewing ignore rules, the baseline, and regular strictness increases.

Steps 1 to 3

Connect analysis without a big bang

Stabilize configuration and signal first, then address the volume of findings.

1. Install and configure one path

  1. Install the tool as a development dependency and commit its version through composer.lock.
  2. Set paths to your src and optionally tests, the PHP version, and an explicit rule level in phpstan.neon.dist.
  3. Run PHPStan in the same container or image as the app. Fix class-not-found, bootstrap, and extension configuration before code findings.
  4. Add a Composer script so developers and CI use the same command without hidden local arguments.
composer require --dev phpstan/phpstan
Official PHPStan Getting Started guide

2. Fix types by value

  1. Start with findings that can alter runtime: wrong arguments, nullable values, missing methods, unreachable branches, and incomplete match expressions.
  2. Add native types to inputs, returns, and properties. Use PHPDoc for generics, array shapes, and types PHP cannot express.
  3. Validate dynamic request, JSON, or database data at the boundary and convert it to a DTO or value object.
  4. Do not silence a problem with broad mixed or blanket ignores. Use an extension, stub, or narrow identified ignore only for proven safe dynamics.
vendor/bin/phpstan analyse --memory-limit=1G
Official PHPStan rule-level documentation

3. Lock legacy debt in a baseline

  1. Choose the highest practical level. If dozens to hundreds of legacy findings remain, generate a one-off baseline and review it manually.
  2. Commit and include the baseline, then reject new errors in CI. Do not regenerate the whole file in routine changes.
  3. Each repaired file removes its matching exception. Track baseline entries as a decreasing metric.
  4. Raise strictness by module or level; hold new code to the current standard without adding historical debt.
vendor/bin/phpstan analyse --level=7 --generate-baseline
Official PHPStan baseline documentation

Step 4

Prove that new errors cannot pass

The same command must be reproducible locally and on a clean CI runner.

  1. Run analysis twice

    Both runs have the same result and configuration does not depend on a locally generated file.

    composer phpstan
  2. Insert an intentional type error

    CI rejects it even in a file with baselined legacy findings. Then revert the change.

  3. Fix one baseline entry

    PHPStan reports an unmatched ignore or you update the baseline narrowly; the exception count falls.

Troubleshooting

Common problems

The first run reports thousands of unknown classes

Fix autoloading, framework extensions, generated stubs, and paths first. Do not baseline a broken configuration.

composer dump-autoload
The baseline grows on every pull request

Generation is not a routine CI step. Commit a fixed baseline and require a separate justification for every new exception.

Developers get a different result from CI

Align the PHP image, extensions, composer.lock, configuration path, and working directory. Cache must not change results.

Analysis memory rises quickly

Do not analyze vendor or cache, update extensions, and inspect problematic files; a higher limit is the last step.

Done

Static analysis now prevents new debt.

Configuration is reproducible, CI catches new type errors, and a controlled baseline lets you remove legacy findings gradually.

Request a call

I will call you on the next working day between 9:00 and 17:00.

You can also call me directly.

+420 605 181 728

Leave your phone number and send a callback request.

By sending, you agree to processing your data in order to handle your request.