Glossary

PHPStan

PHPStan checks type contracts and potential errors before a scenario runs. In a PHP project, it complements tests, coding standards, and ordinary syntax linting.

Short definition

An analyser that understands PHP types and code flow.

The general entry on static code analysis explains the principle. PHPStan is its specific implementation for PHP: it works with native types, PHPDoc, Composer autoloading, and project configuration. From these, it infers which values can reach a given point and whether the code handles them safely.

The tool does not run the application or verify whether a business rule matches the requirements. It can, however, detect a method call on a nullable value, an incorrect argument type, a nonexistent symbol, an invalid return type, an incompletely described collection, or a section of unreachable code. Automated analysis complements human code review because it cannot assess a change’s business meaning or architectural impact.

Use cases

When PHPStan provides the most feedback

It works best where contracts are described precisely and the check runs for every change.

  • refactoring interfaces, DTOs, repositories, and application services
  • checking nullable values, return types, and argument types
  • describing collections and generic helper classes with PHPDoc
  • refining dynamic framework components with extensions or stubs
  • automatically checking a pull request in CI before merging the change

Practical example

Code that PHP will run even though the customer may not exist

The repository method legitimately returns ?Customer because no record may be stored for the external ID. If the application service reads the email without checking, it will work when the customer is found but fail when the customer is missing. PHPStan warns about the possible method call on Customer|null before the import runs.

The fix must express a business decision: create the customer, mark the import as incomplete, or use a method whose contract specifies a particular exception. The analysis itself cannot make that decision.

final class ImportOrder
{
    public function __construct(private CustomerRepository $customers) {}

    public function customerEmail(string $externalId): string
    {
        $customer = $this->customers->findByExternalId($externalId); // ?Customer

        return $customer->email(); // PHPStan: Customer|null
    }
}

How it works

From source files to a finding

The accuracy of the result depends on the quality of the type information, not just the selected level.

  1. Configuration The phpstan.neon file defines the paths to analyse, the rule level, included files, and project settings.
  2. Symbols and metadata PHPStan loads known classes and functions through Composer, native types, PHPDoc, and any framework extensions.
  3. Type inference As it traverses branches, it tracks possible variable values, method returns, and the validity of operations.
  4. Diagnostics A finding identifies the file, line, and error identifier so it can be fixed or, in exceptional, justified cases, ignored.
  5. CI The same command in a pull request stops a change that introduces a newly broken contract.

What PHPStan checks

Type information beyond ordinary PHP runtime checks

It checks technical contracts. It does not format source files or run test scenarios.

Native and inferred types

It evaluates parameters, returns, properties, union and nullable types, and types narrowed by conditions.

PHPDoc and generics

PHPDoc type declarations such as array<int, Order> and tags such as @template or @extends preserve information about collection elements that PHP cannot express in the array type.

Type aliases

A complex recurring data shape can be given an alias so that the contract of a DTO or array remains readable and consistent.

Strictness levels

Levels are cumulative; max denotes the highest level in a given release. Greater strictness reveals more risks but requires more precise types.

Extensions and custom rules

Extensions for Symfony, Doctrine, Nette, PHPUnit, or Larastan improve the understanding of dynamic behaviour. A custom rule can be added for a recurring unwanted pattern specific to a project.

Benefits and limitations

An important check, not proof that an application is correct

Benefits

  • fast feedback before the application is run
  • safer refactoring by tracing dependent contracts
  • more precise types also help the IDE and other team members
  • a repeatable CI check independent of attention during code review

Limitations and mistakes

  • static analysis does not replace unit, integration, or end-to-end tests
  • overly broad mixed types and incomplete PHPDoc reduce the value of the result
  • the highest level alone does not guarantee good domain design
  • global ignores or a thoughtlessly growing baseline can conceal new errors

Configuration and adoption

Strictness should match the team’s ability to resolve findings.

Configuration usually contains the paths to analyse, the level, and include files for extensions. For a new project, it makes sense to set an ambitious level and keep the result free of errors. In an older application, strictness can be increased gradually as long as the team understands what the specific findings mean.

A baseline temporarily suppresses known existing findings and makes it possible to block new ones. But it should not be regenerated automatically on every run: it would lose its role as controlled technical debt. A single legitimate exception is better ignored by error identifier, within a narrow scope, and with an explanation.

Practical guidelines

How to derive real value from the check

The goal is not the highest possible number in the configuration, but trustworthy contracts and fast detection of new regressions.

  • fix the contract at the data source first instead of adding repeated inline @var declarations
  • use native types and PHPDoc for collections or generics
  • choose and update only relevant framework extensions
  • keep the baseline and ignoreErrors small, specific, and justified
  • run the same configuration locally and in CI

Common questions

What to expect from PHPStan

Does PHPStan replace PHPUnit?

No. PHPStan analyses code without running a scenario. PHPUnit runs tests and verifies specific expected behaviour, including collaboration between components according to the chosen test type.

Is PHPStan the same as PHP_CodeSniffer?

No. PHP_CodeSniffer checks coding standards and can fix some findings through PHPCBF. PHPStan evaluates type contracts and possible technical errors; code with perfect style can still contain an incorrect type.

Should a project always use level max?

It is a reasonable choice if the team can keep the result free of new errors and handle tool updates. For an older application, gradually increasing the levels or using a temporary controlled baseline may be better.

When is it appropriate to ignore an error?

Only for a specific, justified exception, such as third-party dynamic behaviour that cannot be described. Fixing the type at the data source, adding a stub or extension, or defining a more precise contract takes priority.

How I use PHPStan in practice

I check type contracts alongside tests and architecture.

I use PHPStan at the highest level with strict rules as part of the regular feedback loop when developing PHP applications.

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.