Adding PHPStan to a legacy project
PHPStan
PHPStan is a static analysis tool for PHP. It helps you find problems in your code base, like passing wrong types, calling methods that don’t exist, detect wrong annotations, and so much more.
I would argue you shouldn’t be writing PHP code these days without having a static analyzer to help you. It reduces the errors you create, and speeds up your development significantly.
How to add PHPStan
1: Composer install
The first thing you’re gonna have to do with PHPStan is to install it. You can run the following command to install it. You will need PHP 7.2 to run and install it, but it can analyze older code if needed.
$ composer require --dev phpstan/phpstan
2: Configuration
You can get started with a really basic configuration file, which is called phpstan.neon
.
The relevant config goes below parameters
. We’re starting with level 0
, which is the lowest level of PHPStan.
At level 0, we are doing very basic checks, like checking for unknown classes/methods, the wrong amount of arguments passed to a method.
# phpstan.neon
parameters:
level: 0
paths:
- src/
- tests/
3: Your first PHPStan run
Once you have PHPStan installed, and a config file set up, you can run PHPStan like this
$ vendor/bin/phpstan a
Now if your project is set up correctly, and you have no problems at level 0, you should see an error like this:
[OK] No errors
But, chances are that your code might not be understandable by PHPStan just yet, and you get errors.
Unknown classes due to custom autoloader
If you have a custom autoloader set up, you can tell PHPStan to use that like follows. This will help PHPStan find the relevant classes. (This file will be executed by PHPStan, so make sure it has no side effects).
parameters:
bootstrapFiles:
- custom_autoloader.php
If you want PHPStan to know about a certain file or directory, but not analyze it, you can add them as follows. This will let PHPStan find all the classes, functions etc from these directories, but not report errors in them.
parameters:
scanFiles:
- ThirdParty.class.php
scanDirectories:
- ./my_classes/custom
And finally, if you have a phar that gets included, that has classes, you can make that phar a bootstrap file as well. This lets PHPStan find its symbols. If you have a PHPUnit phar for example, you can add it like so:
parameters:
bootstrapFiles:
- phpunit.phar
4: Adding it to your CI
If you already have a CI set up, adding PHPStan shouldn’t be too difficult.
I would make sure you have no errors on level 0 before doing so. If there are too many errors check the part of this post
about the baseline. You can run vendor/bin/phpstan a
in your CI, like you would a PHPUnit or any other tool.
At the level 0 we just created, PHPStan won’t have much impact, but it’s good to have it running in your CI already. This makes it so that once we start making PHPStan more strict, we make sure that the rest of your team needs to follow the rules from PHPStan.
5: Increasing the PHPStan levels
At this point you have PHPStan running in your CI, which means that the real work begins. Depending on your code base, just level 2 can already be a difficult task. If at all possible, try to fix all the errors of a level and then move onto the next one. Do this until you start running into too many errors to fix at once. Depending on your code base this can be level 2, 5, 6 or even higher.
Bumping the level is as easy as changing the level value in your config file.
Or you can pass the --level X
flag when you run PHPStan, to use that level for a single call.
6: Adding a baseline
At this point you have PHPStan in your CI, but the amount of errors to fix at once is becoming too much to fix at once. Now you can start introducing a baseline. In PHPStan the baseline is a file containing all your errors, which PHPStan will ignore for you. Now ignoring all the errors may seem counterintuitive at first. You want to fix the problems PHPStan is finding for you, so why ignore it?
The baseline isn’t about fixing all the errors you currently have. Instead, it makes sure that your new code follows the rules you set up. By using a baseline we can increase the checks PHPStan is doing, without having to fix all our code at once. So all the new code you write, and code you change, needs to adhere to the level you have set.
Once you have a baseline set up at your level, you can fix errors in whatever time frame is required. You could choose to fix errors when you touch a file, or maybe take a bit of time every sprint to fix some errors. Depending on how far along you are, you can even bump your level all the way to max level and make a bigger baseline. This will make all your newer code much more type safe, without having to fix all the errors at once.
Getting your team onboard
Usually the biggest problem in adding a tool like PHPStan isn’t adding the tool itself, it’s getting your team onboard with these new demands your tool makes. This is why you can’t just add it at max level with a massive baseline and expect all your colleagues to completely change the way they write code. Instead, you’re going to have to phase it in step by step.
I like to start by adding PHPStan at level 0 or 1 and having it run in CI. This will get your coworkers to get used to the tool occasionally telling them about a big error they made. While this is running you can show them how to run PHPStan for just a specific file, or even for only the changed files in an MR, at a bit of a higher level to catch even more errors. When you feel like they are becoming more familiar with the tool, you can bump it again in CI and fix more issues.
Running PHPStan on only changed files at another level is as easy as running:
vendor/bin/phpstan a $(git diff --name-only) --level <desired level>
Depending on how the adoption in your team goes, you may even have other colleagues asking to increase the PHPStan level, so they can find more errors. And once you’re at a suitably high level you could even consider running PHPStan on bleeding edge. By doing so you are becoming forward compatible with new major versions of PHPStan, and finding even more errors that aren’t the default yet.
Conclusion
If you are introducing PHPStan to your toolkit, be sure to go slow and steady, fix the errors at a lower level, and slowly increase that over time. Do this until your team has adopted it, and knows how to work with it. You can show your colleagues how to properly use it, and show them the advantages of PHPStan, to increase the speed at which the adoption happens.
If you want to get notified of the next blog post, join the newsletter.