Write flawless code with PHPStan

Edouard Courty
3 min readAug 31, 2022

--

Even with a good IDE like PHPStorm, you’ll write code that contains errors.

Untyped properties, missing return type on a function, uninitialised private properties…

Your code will usually run fine until the day when your production server will crash because of a mistake you made three months ago while refactoring legacy code that rarely runs.

That’s what PHPStan has been created for: it finds errors in your code without actually running it, and catches whole classes of bugs even before you write tests for the code.

This is called static analysis.

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 on your server.

Installation

Import PHPStan in your project by using a composer require command:

composer require --dev phpstan/phpstan

And then run it by using its CLI tool:

vendor/bin/phpstan analyse src

This will run PHPStan on your src directory and analyse your code.

PHPStan’s CLI output

Using a configuration file

PHPStan has multiple options that can be passed in the CLI as parameters (such as the level of analysis, paths to be analysed, etc.) but the easiest way of running PHPStan with these options is to use a NEON file.

NEON is similar to YAML. This is what a basic PHPStan configuration file looks like:

parameters:
level: 6
paths:
- src
- tests

The level can be set to any integer between 0 and 9. Here’s a brief overview of what’s checked on each level. Levels are cumulative — for example running level 5 also gives you all the checks from levels 0–4.

  1. Basic checks, unknown classes, unknown functions, unknown methods called on $this, wrong number of arguments passed to those methods and functions, always undefined variables
  2. Possibly undefined variables, unknown magic methods and properties on classes with __call and __get
  3. Unknown methods checked on all expressions (not just $this), validating PHPDocs
  4. Return types, types assigned to properties
  5. Basic dead code checking — always false instanceof and other type checks, dead else branches, unreachable code after return; etc.
  6. Checking types of arguments passed to methods and functions
  7. Report missing typehints
  8. Report partially wrong union types — if you call a method that only exists on some types in a union type, level 7 starts to report that; other possibly incorrect situations
  9. Report calling methods and accessing properties on nullable types
  10. Be strict about the mixed type - the only allowed operation you can do with it is to pass it to another mixed

Running PHPStan with this configuration file is done by adding a -c flag to the command.

vendor/bin/phpstan analyse -c phpstan.neon

Here is the full configuration reference page on PHPStan’s official’s website.

Extensions

PHPStan can take advantage of extensions to better understand some pieces of your code, while using Doctrine / PHPUnit / Prophecy or other libraries…

In the example of Doctrine, some functions like findBy / findOneBy / getRepository are not understood by PHPStan and will produce errors even if the code is correct.

To get started, install PHPStan’s extension installer library. It’ll make the process of adding extensions much faster.

composer require --dev phpstan/extension-installer

Here is a list of official (and some non-official) extensions supported by PHPStan.

Installing extensions is done by simply requiring the composer package, PHPStan’s extension installer will take care of everything else.

composer require --dev phpstan/phpstan-doctrine

Automation

You can include PHPStan in your GitHub Action suite, so your code gets tested every time you push a commit on your PRs.

That’s it!

If you liked this article, make sure to check my other ones, I publish about the PHP ecosystem and development in general. Follow my account to get updated of my future content!

Have a good day :)

--

--

Edouard Courty

Web Developer & IT Teacher based in Paris - Back-end guru - Co-founder of @IMXrarity