As PHP developers, we all want to write clean, maintainable code that doesn’t break in production. But with larger projects, keeping track of potential issues and bugs can become increasingly difficult. This is where tools like static analysis can help out. By analysing your code without actually running it, static analysis can catch potential bugs and improve code quality before it even reaches production.
At Jump24, we’ve found that static analysis has been a powerful tool for improving our code quality as a team. In this post, we’ll discuss how we use PHPStan for static analysis in our day-to-day development, and how it has helped us catch potential issues early in the development process.
Choosing the right static analysis tool
Choosing the right static analysis tool is crucial for any project. We chose PHPStan as the tool to perform static analysis on our code. PHPStan is an open-source PHP static analysis tool that checks for potential errors in your code by analysing the PHP files.
PHPStan is a great choice for us because it has a wide range of rules that can catch potential issues in our code. Additionally, we’ve created custom rules to suit our specific needs. For example, we’ve created custom rules that enforce our internal coding standards and prevent certain types of mistakes that we’ve seen constantly in codebases we’ve been asked to take over and maintain. When we were comparing PHPStan and Psalm we found that PHPStan had much better support for Laravel thanks to Larastan, so whenever we’re using PHPStan we also install Larastan at the same time.
Using PHPStan in day-to-day development
Using PHPStan in our day-to-day development process has been crucial for catching potential issues before they make it to production. For us to achieve this there are a few steps that we first need to take to be able to use PHPStan in our project.
Setting up PHPStan
Firstly you will need to install PHPStan a detailed explanation of how to do that can be found here
composer require --dev phpstan/phpstan
After this you can either run the command with the required parameters or as we do, create a phpstan.neon file which holds the configuration for your static analysis.
Example phpstan.neon File
parameters:
level: 9
paths:
- app
- config
- routes
- database/factories
reportUnmatchedIgnoredErrors: true
checkMissingIterableValueType: false
checkModelProperties: true
checkUnusedViews: false
excludePaths:
- tests
- vendor
- _ide_helper.php
- _ide_helper_models.php
- .phpstorm.meta.php
- node_modules
As you can see in this file we’re setting the level of analysis we would like to run on the code base (highest is 9), the paths we would like the tool to analyse and we have set a few small config items that we choose to have set by default in our code base. There is also a excludePaths section which allows us to tell the tool not to analyse the files or folders listed here.
As well as this it’s possible for your phpstan.neon file to include another file. At Jump24 we have a base phpstan.neon file that exists in our standards package that we use in all our Laravel projects. This file sets up a number of basic rules that we always want to have present in our analysis of a codebase.
Once we’ve installed the package and added the relevant file we then use a simple composer command to run PHPStan on our codebase:
First update your composer.json file adding the following into the scripts section
"scripts": {
"phpstan": [
"Composer\\Config::disableProcessTimeout",
"vendor/bin/phpstan analyse --memory-limit=4G"
],
}
Then run the following command in your terminal to analyse your code.
composer phpstan
This command runs PHPStan on all PHP files in your project that have been defined in your configuration file from above, and outputs a report of any potential issues it finds.
One of the benefits of using PHPStan is that it can catch potential issues with type hinting and type safety. For example, consider this very simple function that adds two integers together:
function addTwoNumbers(int $numberOne, int $numberTwo): int {
return $numberOne + $numberTwo;
}
$result = addTwoNumbers(1, '2');
This code will run without any errors, but it’s clearly incorrect as we’re using the wrong type for the second parameter being passed to the method addTwoNumbers
, we’re passing a string when the method requires a int. Due to PHP not being a strict language by default this code will still run and you will get the correct result. Now if you make the following change to the code above.
declare(strict_types=1);
function addTwoNumbers(int $numberOne, int $numberTwo): int {
return $numberOne + $numberTwo;
}
$result = addTwoNumbers(1, '2');
And then try and run the code you will get the following error
Fatal error: Uncaught TypeError: Argument 2 passed to addTwoNumbers() must be of the type int, string given
If you run this code through your static analysis tool you will get an error similar to this
Parameter #2 $numberTwo of function addTwoNumbers() expects int, string given.
Though this is a simple example it shows how important using tools like this is and how easy it is for type errors to slip in. As soon as you turn strict_types=1
on for your codebase you might start to see more and more of this type of error occur, particularly when working with an old codebase that you’re trying to modernise. This is where tools like Rector also become super useful.
Adding to an existing project
If you’re taking over a project or you have a an old project that you’d like to add PHPStan to but you’re afraid of the amount of errors you might get when you go to run the command don’t fret, you can start off with a baseline file that takes the current errors and puts them into this file which you will then add to your phpstan.neon file as shown below
includes:
- phpstan-baseline.neon
parameters:
level: 9
paths:
- app
- config
- routes
- database/factories
reportUnmatchedIgnoredErrors: true
checkMissingIterableValueType: false
checkModelProperties: true
checkUnusedViews: false
excludePaths:
- tests
- vendor
- _ide_helper.php
- _ide_helper_models.php
- .phpstorm.meta.php
- node_modules
by adding this file the next time you run static analysis PHPStan will not warn you about the current errors and only on new errors on the new code you write and change. This gives you the ability to quickly add it to these kind of projects and improve them over time. Head to the PHPStan docs to find out more about the baseline
Continuous Integration with PHPStan
Running your static analysis tool during your development lifecycle is very important to help you pickup issues early and keep that feedback loop small. It’s also really important to make sure your CI tools are also running the same analysis of your code to make sure that nothing slips through. We typically use Github for our code repositories so we have Github Actions setup running analysis on each PR that a developer puts in.
Here is a typical example of a very basic Github Action workflow that runs PHPStan:
name: PHPStan
on: pull_request
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: composer install
- name: Run PHPStan
run: composer run phpstan
By having our CI run the analysis we’re stopping issues from getting into our releases which helps us maintain a higher standard of coding.
Improved code quality as a team
Overall using tools like PHPStan has been a game-changer for the team. By catching potential issues early in the development process, we’ve been able to avoid potential bugs in production and ensure that our code is much stronger typed, after all who doesn’t like a stronger typed codebase?