Skip to content

Liber Tertius · Chapter 3 · Keeping a project current

Analyse a project with PHPStan

grim-core ships PHPStan, its configuration and the baseline the monorepo keeps. A project runs the same analysis with one overlay, phpstan-instance.neon, and has to run it where the application boots, which is the app container.

≈10 min 3 steps A running stack A project on core 1.3.25 or later
Source

Before you start

The stack is up (grim up), and the project is on core 1.3.25 or later. From that release grim-core carries three files next to composer.json:

phpstan.neon the monorepo's configuration: level, paths, Larastan
phpstan-baseline.neon findings the monorepo grandfathers, every module's
phpstan-instance.neon phpstan.neon with unmatched baseline entries allowed

The baseline names files of every module in the monorepo. Your project installs a few of them, so most entries match nothing, and plain phpstan.neon stops on the first "is neither a directory, nor a file path". phpstan-instance.neon is the same configuration with that check switched off. Always analyse through it.

The ritual, in order

Three steps. The first is the whole analysis; the other two are about reading it and trusting it next time.

  1. Run it in the app container

    Larastan boots the application to learn what app('…') returns, what a model's columns are and which config keys exist. The application boots only where its .env points, and that is the stack: on your machine redis and mysql are hostnames of nothing, and the analysis dies with "Laravel framework bootstrap failed".

    grim composer runs in the app container, and core's composer.json has an analyse script. Everything after the second -- goes to PHPStan:

    $ grim composer -- analyse -- -c phpstan-instance.neon --no-progress
     …
      101    Access to an undefined property Modules\Users\Models\User::$planning_role.
     …
     [ERROR] Found 4 errors

    The first -- ends grim's options, the second ends Composer's. Without the second one Composer swallows -c and PHPStan reads plain phpstan.neon.

    A whole project takes about a minute. To look at one module, name it:

    $ grim composer -- analyse -- -c phpstan-instance.neon --no-progress src/Registry
    
     [OK] No errors
  2. Tell your finding from an inherited one

    A finding in a file you changed is yours: fix it before you push.

    A finding in a module you did not touch usually comes from what the project installs. The monorepo has every module enabled and every migration run; your project does not. The common case is a core model reading a column a module you do not install adds:

    src/Users/Models/User.php:101  Access to an undefined property Modules\Users\Models\User::$planning_role.

    Larastan learns the columns from the migrations on disk. planning_role is added by the Planning module, which this project does not install. That is a question for whoever maintains the module the finding is in (does it really need a column another module adds?), not something to fix in your project. Report it to them with the line PHPStan printed.

  3. Keep the cache honest

    PHPStan caches results in var/phpstan per file. A file that did not change keeps its old result even when what it depends on did, so after grim update, or after enabling or removing a module, clear the cache:

    $ grim composer -- exec phpstan -- clear-result-cache -c phpstan-instance.neon
    
    Result cache cleared from directory:
    /var/www/html/var/phpstan

    Clearing reads the configuration too, so it needs -c phpstan-instance.neon like the analysis does.

What you have now

The project analysed with the configuration and baseline of its train, in the environment the application runs in. What is left is either yours or a question for a module's maintainer. The unit check on a pull request runs the same analysis over one module; see Read the unit check on your pull request.

When it does not work

Laravel framework bootstrap failed. The analysis ran outside the container, or the stack is down. Run it through grim composer with the stack up.

is neither a directory, nor a file path, nor a fnmatch pattern, for files of modules you do not have. PHPStan read phpstan.neon, not the overlay. Check the second --.

phpstan-instance.neon does not exist. The project is on core older than 1.3.25. Bring it to 1.3.25 or later with grim update.

The process is killed with no message. The container ran out of memory. The analyse script asks for 4 GB; give Docker more, or analyse one module at a time.