Get started with Psalm on a long-living project
Static analysis tools like Psalm and PHPStan are gaining popularity in the PHP world. They provide us with the ability to detect potential bugs in our code without actually executing it.
However, when introducing these tools into a long-lived project, several potential challenges may arise. Your initial run might yield a daunting list of over 3000 errors like this:
$ ./vendor/bin/psalmScanning files...Analyzing files... ░░░░░░░░░░░░░░░░░░E░░E░░░░░░░░░E░░░░░░░░░░░░E░░░░E░░░░░░░ 30 / 1065 (5%)░░░░░░░░░░░░░E░░░░░░░░░░░░░░░░░░░░░░░░░E░░░░░░░░░░E░░░░░░ 60 / 1065 (11%){{ continues }} {{ list of errors }} ------------------------------3000 errors found------------------------------
While you can exclude specific directories from the analysis and fix many errors, there may still be some errors you can't or don't want to address immediately.
Nevertheless, it's crucial to receive positive feedback from the terminal when running the tool, indicating that no errors have been found. Leaving these errors unhandled can lead to confusion, especially when working with co-workers on the project. Even if there's disagreement about whether certain errors should be fixed, they should be excluded from the results.
But how can we achieve this? Wouldn't it be convenient to start using Psalm on a project while ignoring a group of existing errors that you don't intend to fix all at once?
$ ./vendor/bin/psalmScanning files...Analyzing files... ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 60 / 1065 (5%)░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 120 / 1065 (11%)░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 180 / 1065 (16%)░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 240 / 1065 (22%)░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 300 / 1065 (28%)░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 360 / 1065 (33%)░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 420 / 1065 (39%)░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 480 / 1065 (45%){{ continues }} ------------------------------No errors found!------------------------------
Level strictness
Psalm operates with different levels of strictness, ranging from level 1 (most strict) to level 8 (most lenient). When you initialize Psalm, it will "guess" a default strictness level and set it in your Psalm config file.
<?xml version="1.0"?><psalm errorLevel="3" resolveFromConfigFile="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd">
I wouldn't advise using a high strictness level when you're just getting started with Psalm. Psalm is a tool designed to help you write safer code, not to irritate you or your colleagues. You can always increase the strictness level as you become more comfortable.
Lowering the default strictness level will reduce the number of errors:
------------------------------226 errors found------------------------------
However, this approach may compromise code safety, as fewer errors will be detected later on. I usually opt for level 5 in my Laravel projects, considering the project's scale and age. Choose the level that suits your comfort level without making Psalm feel burdensome. If you want to learn how to use Psalm in Laravel projects, I recommend watching this Spatie video.
Suppressing
You have two methods to silence errors. You can suppress a single error inline using docblocks:
<?php/** @psalm-suppress InvalidReturnType */function (int $a) : string{ return $a;}
But if the same errors occur multiple times in your codebase, you could suppress these in the config file with the <issueHandlers>
tag:
<issueHandlers> <!-- Suppressed because Laravel models provide magic properties --> <NoInterfaceProperties errorLevel="suppress" /></issueHandlers>
Keep in mind that this approach will also suppress future issues. We want to address existing problems while treating new issues as new errors.
Baseline file
A better alternative for managing errors is to use a baseline file:
vendor/bin/psalm --set-baseline=your-baseline.xml
This command generates an XML file that contains information about the current errors and their exact locations in the project:
<?xml version="1.0" encoding="UTF-8"?><files psalm-version="4.1.1@16b..."> <file src="app/Console/Commands/Development/DumpSchemaCommand.php"> <PossiblyNullReference occurrences="1"> <code>makeCurrent</code> </PossiblyNullReference> </file> ...</files>
By committing this generated file, you ensure that Psalm running in other environments (e.g., CI) won't complain about these known errors. Any new code changes introducing errors not listed in the baseline file will cause the build to fail.
Baseline files provide an excellent way to gradually improve a codebase. You can always update your baseline file if you make any fixes:
vendor/bin/psalm --update-baseline