PHP: Psaml
Tags: php, tutorial, productivity
Published: 2023-10-09 20:36:22

### About Psalm


Psalm is a static analysis tool for finding errors in PHP applications.

It has a few features that go further than other similar tools:

- **Mixed type warnings**

    If Psalm cannot infer a type for an expression then it uses a `mixed` placeholder type. `mixed` types can sometimes mask bugs, so keeping track of them helps you avoid a number of common pitfalls.

- **Intelligent logic checks**

    Psalm keeps track of logical assertions made about your code, so `if ($a && $a) {}` and `if ($a && !$a) {}` are both treated as issues. Psalm also keeps track of logical assertions made in prior code paths, preventing issues like `if ($a) {} elseif ($a) {}`.

- **Property initialisation checks**
    
    Psalm checks that all properties of a given object have values after the constructor is called.

- **Taint analysis**

    Psalm can [detect security vulnerabilities](https://psalm.dev/articles/detect-security-vulnerabilities-with-psalm) in your code.

- **Language Server**

    Psalm has a Language Server that’s [compatible with a range of different IDEs](https://psalm.dev/docs/running_psalm/language_server/).

- **Automatic fixes**

    Psalm can [fix many of the issues it finds automatically](https://psalm.dev/docs/manipulating_code/fixing/).

- **Automatic refactoring**

    Psalm can also [perform simple refactors](https://psalm.dev/docs/manipulating_code/refactoring/) from the command line.

### Installation

The latest version of Psalm requires PHP >= 7.4 and Composer.

```
composer require --dev vimeo/psalm
```

Generate a config file:

```
./vendor/bin/psalm --init
```
Psalm will scan your project and figure out an appropriate [error level](https://psalm.dev/docs/running_psalm/error_levels/) for your codebase.

Run psalm for entire project:

```
./vendor/bin/psalm
```

Or for specific files:

```
./vendor/bin/psalm src/Controller
```

Save results in XML:

```
./vendor/bin/psalm --report=checkstyle.xml
```

Psalm will probably find a number of issues - find out how to deal with them in [Dealing with code issues](https://psalm.dev/docs/running_psalm/dealing_with_code_issues/).

### Error levels

You can run Psalm in at different levels of strictness from 1 to 8.

Level 1 is the most strict, level 8 is the most lenient.

When no level is explicitly defined, psalm defaults to level 2.

Some issues are always treated as errors. These are issues with a very low probability of false-positives.

At level 1 all issues (except those emitted for opt-in features) that Psalm can find are treated as errors. Those issues include any situation where Psalm cannot infer the type of a given expression.

At level 2 Psalm ignores those Mixed* issues, but treats most other issues as errors.

At level 3 Psalm starts to be a little more lenient. For example Psalm allows missing param types, return types and property types.

At level 4 Psalm ignores issues for possible problems. These are more likely to be false positives – where the application code may guarantee behaviour that Psalm isn't able to infer.

Level 5 and above allows a more non-verifiable code, and higher levels are even more permissive.

### Dealing with code issues

Psalm has a large number of [code issues](https://psalm.dev/docs/running_psalm/issues/). Each project can specify its own reporting level for a given issue.

Code issue levels in Psalm fall into three categories:

- `error`

    This will cause Psalm to print a message, and to ultimately terminate with a non-zero exit status.

- `info`

    This will cause Psalm to print a message.

- `suppress`

    This will cause Psalm to ignore the code issue entirely.

### Basic Usage

```
./vendor/bin/psalm --init scr/ 8
```
The first parameter tells Psalm where to find our source code, and the second is the strictness level.

```
./vendo/bin/psaml file.php
```

```
Target PHP version: 8.2 (inferred from current PHP version).
Scanning files...
Analyzing files...

E

ERROR: UnusedClass - index.php:3:8 - Class User is never used (see https://psalm.dev/075)
	class User{


ERROR: MissingConstructor - index.php:4:18 - User has an uninitialized property User::$name, but no constructor (see https://psalm.dev/073)
		private string $name;


ERROR: MissingConstructor - index.php:5:19 - User has an uninitialized property User::$products, but no constructor (see https://psalm.dev/073)
    private array $products;


ERROR: MissingReturnType - index.php:7:21 - Method User::Add does not have a return type, expecting void (see https://psalm.dev/050)
    public function Add(){


ERROR: MixedAssignment - index.php:8:6 - Unable to determine the type that $this->name is being assigned to (see https://psalm.dev/032)
    	$this->name = $name;


ERROR: UndefinedVariable - index.php:8:20 - Cannot find referenced variable $name (see https://psalm.dev/024)
    	$this->name = $name;

------------------------------
6 errors found
------------------------------
Psalm can automatically fix 1 of these issues.
Run Psalm again with 
--alter --issues=MissingReturnType --dry-run
to see what it can fix.
------------------------------
```
The analysis report resulted in 6 errors and 1 of them can be fixed automatically.

Automatically fixing errors:

```
./vendor/bin/psalm --alter --issues=MissingReturnType file.php
```

```
Target PHP version: 8.2 (inferred from current PHP version).
Scanning files...
Analyzing files...

░
Altering files...
Altered /home/zero/Documents/Linguagens/PHP/Psaml/index.php
------------------------------
                              
       No errors found!       
                              
------------------------------

```

Sometimes this may appear in the report:

```
------------------------------
10 other issues found.
You can hide them with --show-info=false
------------------------------
```
It did find 10 other issues, reported as INFO lines in the output. Anything reported as INFO at a lower strictness level gets reported as an error at higher strictness levels, because we plan on working up through the strictness its safe for us to ignore these INFO issues.

To not show the INFO:

```
./vendor/bin/psalm --show-info=false file.php
```

To show the INFO:

```
./vendor/bin/psalm --show-info=true file.php
```