Static analysis met PHPStan en Psalm uitgelegd

Leer hoe static analysis met PHPStan en Psalm werkt. Praktische uitleg over types, levels en configuratie voor betrouwbaardere PHP-code in 2026.

26 juni 20267 min leestijdDoor We Develop Communication

Bugs vinden vóórdat je code ook maar één keer draait, dat is precies wat static analysis met PHPStan en Psalm mogelijk maakt. Deze tools lezen je PHP-code als een kritische collega: ze wijzen je op typefouten, onbereikbare branches, verkeerd gebruikte argumenten en null-pointer issues die je anders pas in productie zou ontdekken.

In moderne PHP-projecten zijn PHPStan en Psalm onmisbaar geworden. Samen met PHP 8.x features zoals readonly properties en enums til je de betrouwbaarheid van je codebase naar een niveau dat voorheen alleen statisch getypte talen haalden.

In dit artikel leer je hoe static analysis werkt, wat de verschillen zijn tussen PHPStan en Psalm, en hoe je ze praktisch inzet in je project.

Wat is static analysis?

Static analysis (statische code-analyse) is het proces waarbij een tool je broncode analyseert zonder die uit te voeren. De analyzer leest je bestanden, bouwt een model van types en control flow op, en controleert dat model op inconsistenties.

Het verschil met dynamische analyse is belangrijk. Unit tests, zoals uitgelegd in testing in PHP, draaien je code daadwerkelijk en controleren gedrag. Static analysis daarentegen kijkt alleen naar wat er op papier staat.

Waar static analysis in uitblinkt:

  • Typefouten opsporen (verkeerde argumenten, return types)
  • Null-dereferences detecteren
  • Onbereikbare code vinden
  • Ongebruikte variabelen en imports melden
  • Verkeerd gebruik van API's signaleren

Je krijgt een soort "compiler-check" voor PHP, een taal die van oorsprong geen strikte compile-fase kent.

Waarom static analysis in PHP?

PHP is historisch gezien een losjes getypeerde taal. Variabelen kunnen van type veranderen, functies accepteren regelmatig mixed input en null sluipt makkelijk in onverwachte plekken. Dat maakt PHP flexibel, maar ook foutgevoelig.

Sinds PHP 7 en zeker met PHP 8.x is de typesituatie sterk verbeterd: union types, intersection types, readonly properties en enums geven je veel meer grip. Static analysis tools benutten deze metadata om diepgaande checks uit te voeren die de PHP-interpreter zelf niet doet.

Een paar veelvoorkomende bugs die een tool als PHPStan direct vangt:

  • Een methode aanroepen op een object dat null kan zijn
  • Een array-sleutel gebruiken die niet gegarandeerd bestaat
  • Een functie aanroepen met het verkeerde aantal argumenten
  • Inconsistente return types tussen subclasses

Deze bugs kosten in productie vaak veel meer tijd dan de paar seconden die de analyzer nodig heeft.

PHPStan: de populairste keuze

PHPStan, ontwikkeld door Ondřej Mirtes, is inmiddels de meest gebruikte static analysis tool in de PHP-wereld. De tool is pragmatisch, snel en heeft een enorm ecosysteem aan extensies voor frameworks als Laravel, Symfony en Doctrine.

Installatie

Installeer PHPStan eenvoudig via Composer:

composer require --dev phpstan/phpstan

Maak een configuratiebestand phpstan.neon aan in de root van je project:

parameters:
    level: 5
    paths:
        - src
        - tests
    excludePaths:
        - vendor

Run vervolgens de analyse:

vendor/bin/phpstan analyse

Levels van 0 tot 10

Het krachtige aan PHPStan is het level-systeem. Je start op niveau 0 (basale checks) en werkt op richting niveau 10 (maximaal strikt). Elk niveau voegt extra regels toe:

  • Level 0: basis syntax checks, undefined classes
  • Level 2: unknown methods op objecten
  • Level 5: argumenttypes worden gecontroleerd
  • Level 8: nullable types moeten expliciet afgehandeld worden
  • Level 10: strikste controle, inclusief mixed types

Deze gelaagde aanpak maakt PHPStan geschikt voor zowel legacy projecten als greenfield codebases.

Voorbeeld van een melding

Stel je hebt deze functie:

function getUserName(?User $user): string
{
    return $user->getName();
}

PHPStan meldt op level 8:

Cannot call method getName() on User|null.

Dat is precies het soort null-bug dat je in productie een 500-error oplevert. Fix het door expliciet te checken:

function getUserName(?User $user): string
{
    return $user?->getName() ?? 'Anoniem';
}

Psalm: de strenge alternatief

Psalm, ontwikkeld door Vimeo, is de andere grote speler. Psalm voelt vaak strenger aan, met name rond nullability, immutability en type-narrowing. Ook heeft Psalm ingebouwde security-checks via "taint analysis" die bijvoorbeeld SQL injection of XSS risico's kan opsporen.

Installatie

composer require --dev vimeo/psalm
vendor/bin/psalm --init

Het --init commando scant je project en stelt automatisch een startlevel voor. Psalm gebruikt levels 1 tot 8, waarbij 1 het strikst is (omgekeerd van PHPStan).

Sterke punten van Psalm

  • Taint analysis: detecteert onveilige data-paden (user input → SQL query)
  • Immutability checks: markeer klassen als @psalm-immutable
  • Advanced generics: uitgebreide ondersteuning voor template types
  • Type aliases: definieer herbruikbare types in docblocks

Psalm is vooral waardevol in projecten waar security en strikte types belangrijk zijn, zoals API-platformen en financiële systemen. Bekijk ook validatie en security basics voor meer context over veilig programmeren.

PHPStan vs Psalm: welke kies je?

Beide tools doen fundamenteel hetzelfde werk. De keuze hangt af van je project en voorkeuren.

Aspect PHPStan Psalm
Populariteit Zeer hoog Hoog
Ecosysteem Groot, veel extensies Kleiner, wel rijk
Taint analysis Via extensies Ingebouwd
Configuratie NEON-formaat XML
Leercurve Gematigd Steiler
Snelheid Zeer snel Snel

Voor de meeste projecten is PHPStan de pragmatische eerste keus. Heb je security-kritische code of wil je maximale strictness? Dan loont Psalm.

Sommige teams gebruiken beide. Dat geeft de beste dekking maar verdubbelt onderhoud op configuratie en baselines.

Docblocks en generics

Beide tools halen veel informatie uit PHPDoc-annotaties. Ook als PHP zelf een type niet native ondersteunt, kun je het documenteren:

/**
 * @param list<User> $users
 * @return array<string, int>
 */
function countUsersByRole(array $users): array
{
    // ...
}

list<User> betekent: een array met oplopende integer keys en alleen User objecten. array<string, int> is een associatieve array met string-keys en integer-values. Dit soort generics zijn essentieel voor betrouwbare analyse van collecties en arrays in PHP.

Integratie in je workflow

Static analysis werkt alleen als je het consequent draait. Integreer de tools op drie plekken:

1. Lokaal tijdens development

Voeg scripts toe aan composer.json:

"scripts": {
    "analyse": "phpstan analyse --memory-limit=1G",
    "psalm": "psalm --no-cache"
}

Zo run je de analyse met composer analyse voordat je commit.

2. In je CI-pipeline

Laat elke pull request automatisch geanalyseerd worden. GitHub Actions, GitLab CI en Bitbucket Pipelines ondersteunen dit probleemloos. Een PR die niet door de analyse komt, mergt niet.

3. Pre-commit hooks

Tools als Husky of captainhook/captainhook laten je de analyse draaien vóór elke commit. Kleinschalig maar effectief.

De baseline: static analysis toevoegen aan legacy code

Een bestaand project met duizenden regels code gaat niet zomaar door level 8 PHPStan. Beide tools ondersteunen een baseline: een bestand met alle huidige meldingen als "bekende issues".

Voor PHPStan:

vendor/bin/phpstan analyse --generate-baseline

Dit produceert phpstan-baseline.neon. Alle bestaande meldingen worden genegeerd, maar nieuwe code moet voldoen aan het ingestelde level. Zo voorkom je verdere degradatie en kun je stap voor stap de baseline verkleinen.

Dezelfde aanpak werkt voor Psalm via psalm --set-baseline=psalm-baseline.xml.

Static analysis en architectuur

Strikte typing dwingt je richting betere architectuur. Als je werkt met SOLID principes, design patterns of domain-driven design, dan versterkt static analysis die patronen.

Value objects worden vanzelf consistent, aggregates krijgen duidelijke interfaces, en services met afhankelijkheden worden door PHPStan of Psalm bewaakt op correct gebruik. Het is eigenlijk een gratis reviewer die nooit moe wordt.

Veelgemaakte fouten bij het invoeren

Let op deze valkuilen:

  • Te hoog starten: level 8 op een legacy project geeft je duizenden meldingen. Start laag.
  • Baseline als permanente oplossing zien: de baseline is een tijdelijke concessie, niet een einddoel.
  • Alleen lokaal draaien: zonder CI-integratie sluipt er toch gebroken code in main.
  • Docblocks verwaarlozen: zonder goede annotaties mist de tool context.
  • mixed accepteren: probeer concrete types te gebruiken waar mogelijk.

Bekijk voor verdere verdieping ook de officiële PHPStan documentatie en de Psalm documentatie.

Veelgestelde vragen

Wat is static analysis in PHP?

Static analysis is een techniek waarbij je broncode analyseert zonder hem uit te voeren. Tools zoals PHPStan en Psalm detecteren typefouten, onbereikbare code en bugs voordat je code draait.

Wat is het verschil tussen PHPStan en Psalm?

Beide tools doen hetzelfde werk, maar Psalm is vaak strenger rond nullability en heeft sterkere security-checks. PHPStan is populairder, heeft meer extensies en een eenvoudiger configuratie.

Vervangt static analysis unit tests?

Nee, ze vullen elkaar aan. Static analysis vangt type- en logicafouten af, terwijl unit tests het daadwerkelijke gedrag van je code controleren. Je hebt beide nodig voor robuuste applicaties.

Op welk level moet ik starten met PHPStan?

Begin op level 0 of 1 en verhoog stapsgewijs. Zo voorkom je honderden meldingen tegelijk en kun je je codebase gecontroleerd opschonen naar het maximum van level 10.

Kan ik static analysis toevoegen aan een bestaand project?

Ja, beide tools ondersteunen een baseline-bestand. Je legt de huidige meldingen vast als bekend probleem en dwingt nieuwe code op een hoger niveau af, zonder direct alles te hoeven fixen.

Veelgestelde vragen

Klaar om digitaal te groeien?

Wij helpen Nederlandse bedrijven met webtechnologie en SEO-strategieën die écht werken. Neem vrijblijvend contact op.