> ## Content Index
> Fetch the complete content index at: https://madewithlove.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Catching bugs early with static analysis
- URL: https://madewithlove.com/blog/how-to-catch-bugs-early-with-static-analysis-using-psalm-phan-or-phpstan/
- Published: 2019-08-12T13:16:13.000Z
- Updated: 2026-05-29T11:58:11.000Z
- Author: Bram Devries
- Tags: Engineering, Code quality, Tutorials

PHP is a dynamic language and while this certainly has its benefits, it also means that it’s not uncommon to see errors about calling an undefined method, or an invalid argument count in the logs. What’s even worse is that the application will simply crash when these types of mistakes occur, resulting in a bad user experience and frustrated customers.

The solution to this problem is static analysis. For languages such as Java and C# this isn’t something new, it is a built-in feature that gets performed whenever the code is compiled.

Recently various tools have popped up that allow running static analysis on PHP code. To name a few: [Psalm](https://psalm.dev/), [Phan](https://github.com/phan/phan) and [PHPStan](https://github.com/phpstan/phpstan). The goal of these tools is to reduce the amount of errors before the first test is written.

In this blog post I want to focus more on the latter since it’s what we have been using the most at madewithlove.

## Getting started with static analysis

The tool can be installed into any project through Composer.

```bash
composer require --dev phpstan/phpstan
```

PHPStan requires PHP 7.1 to be installed on the environment in order to run. But the code it needs to analyze can be written for PHP 5.6\. Having properly typed code through either type hints or docblocks will lead to better results.

Once installed, the binary can be used to analyze a directory and output any mistakes encountered.

```bash
 vendor/bin/phpstan analyze src
```

```bash
------ -------------------------------------------------------------------------
  Line   Abstracts/AbstractIndex.php
 ------ -------------------------------------------------------------------------
  66     Method ElasticSearcher\Abstracts\AbstractIndex::setTypes() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  74     Method ElasticSearcher\Abstracts\AbstractIndex::getTypes() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  84     Method ElasticSearcher\Abstracts\AbstractIndex::setSettings() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  92     Method ElasticSearcher\Abstracts\AbstractIndex::getSettings() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  102    Method ElasticSearcher\Abstracts\AbstractIndex::getType() should return
         array but returns $this(ElasticSearcher\Abstracts\AbstractIndex).
 ------ -------------------------------------------------------------------------

 ------ ---------------------------------------------------------------------
  Line   Abstracts/AbstractQuery.php
 ------ ---------------------------------------------------------------------
  93     PHPDoc tag @param has invalid value (null|string): Unexpected token
         "\n\t ", expected TOKEN_VARIABLE at offset 48
 ------ ---------------------------------------------------------------------

 ------ ---------------------------------------------
  Line   ElasticSearcher.php
 ------ ---------------------------------------------
  75     Negated boolean expression is always false.
  87     Negated boolean expression is always false.
 ------ ---------------------------------------------

 ------ -------------------------------------------------------------------
  Line   Managers/DocumentsManager.php
 ------ -------------------------------------------------------------------
  132    Method ElasticSearcher\Managers\DocumentsManager::exists() should
         return bool but returns array|bool.
 ------ -------------------------------------------------------------------

 [ERROR] Found 9 errors
```

I analyzed our popular [Elasticsearcher](https://github.com/madewithlove/elasticsearcher) package and got the following result. 

PHPStan has different rule levels, each of them more strict than the one before. By default it will use level 0 which check for “obvious” mistakes such as extra arguments being passed or syntax errors. This level is a good place to start for legacy codebases and over time the level can be increased for more stricter analysis. There is also a *max* level that is aimed at static purists, which will always run the highest level available.

It’s possible that PHPStan outputs certain errors that are not really errors but the result of magic behavior added by a third-party package or framework. These errors can be fixed by installing the relevant [extension](https://github.com/phpstan/phpstan#extensibility) through Composer and configuring it in a `phpstan.neon` file.

```bash
includes:
    - ../vendor/phpstan/phpstan-doctrine/extension.neon
```

The `phpstan.neon` file also allows configuring a lot of different parameters. One very common setting is the `ignoreErrors` parameter. This allows to ignore errors based on regular expressions. Let’s say that we want to ignore the `Negated boolean expression is always false`. error from our previous run, we can do so by adding the following:

```bash
parameters:
    ignoreErrors:
       - '#Negated boolean expression is always false.#'
```

If we ever do end up fixing this reported error and it no longer occurs PHPStan will warn us that the ignored error has become obsolete.

```bash
------ -------------------------------------------------------------------------
  Line   Abstracts/AbstractIndex.php
 ------ -------------------------------------------------------------------------
  66     Method ElasticSearcher\Abstracts\AbstractIndex::setTypes() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  74     Method ElasticSearcher\Abstracts\AbstractIndex::getTypes() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  84     Method ElasticSearcher\Abstracts\AbstractIndex::setSettings() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  92     Method ElasticSearcher\Abstracts\AbstractIndex::getSettings() should
         return array but returns
         $this(ElasticSearcher\Abstracts\AbstractIndex).
  102    Method ElasticSearcher\Abstracts\AbstractIndex::getType() should return
         array but returns $this(ElasticSearcher\Abstracts\AbstractIndex).
 ------ -------------------------------------------------------------------------

 ------ ---------------------------------------------------------------------
  Line   Abstracts/AbstractQuery.php
 ------ ---------------------------------------------------------------------
  93     PHPDoc tag @param has invalid value (null|string): Unexpected token
         "\n\t ", expected TOKEN_VARIABLE at offset 48
 ------ ---------------------------------------------------------------------

 ------ -------------------------------------------------------------------
  Line   Managers/DocumentsManager.php
 ------ -------------------------------------------------------------------
  132    Method ElasticSearcher\Managers\DocumentsManager::exists() should
         return bool but returns array|bool.
 ------ -------------------------------------------------------------------

 ---------------------------------------------------------------------------------------------------------
  Error
 ---------------------------------------------------------------------------------------------------------
  Ignored error pattern #Negated boolean expression is always false.# was not matched in reported errors.
 ---------------------------------------------------------------------------------------------------------

 [ERROR] Found 8 errors
```

 Be sure to read through[ the full documentation](https://github.com/phpstan/phpstan#configuration) to see what’s possible.

On a lot of our projects, PHPStan is included as part of the CI pipeline. Doing this means that when developers are writing and reviewing code they can focus more on the core functionalities instead of worrying about syntax and type errors making it to production.