# Statische analyse: PHPStan en Psalm

> PHPStan en Psalm uitgelegd: niveaus, baseline, taint-analyse en wat statische analyse voor veilige PHP-toepassingen kan doen en wat niet.

Bron: https://phpsos.de/nl/php/statische-analyse/  
Stand: 25-10-2026  
Aanbieder: PHP SOS, PHP-noodhulp en serverbeveiliging. Directe hulp via WhatsApp: https://wa.me/491736494914?text=Hallo%20PHP%20SOS%2C%20wij%20hebben%20hulp%20nodig%20met%20onze%20PHP-server.%20Bedrijf%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kort gezegd:** Statische analyse controleert PHP-code zonder die uit te voeren en vindt zo typefouten, ongedefinieerde variabelen, dode code en aanroepen op mogelijk lege waarden. De twee verbreide hulpmiddelen zijn PHPStan (niveaus 0 tot 10) en Psalm (foutniveaus 1 tot 8, plus een taint-analyse voor onveilige gegevensstromen). Ze vinden fouten in uw toepassing, niet in de PHP-interpreter; tegen lekken zoals PHP2Shell helpt alleen de PHP-update.

## Wat is statische analyse?

Een test voert code uit en vergelijkt het resultaat. Statische analyse leest de code daarentegen als een zeer grondige controleur en leidt uit typen, retourwaarden en aanroepen af waar iets mis kan gaan. Ze vindt fouten ook in takken die geen enkele test ooit bereikt.

Voor beslissers is het nut eenvoudig: fouten die vóór de livegang worden gevonden, kosten een fractie van fouten die klanten opmerken. Voor ontwikkelaars is het een vaste metgezel die in de ontwikkelomgeving en in de CI-pipeline meeloopt. Slaat de analyse bij een wijziging aan, dan wordt die niet overgenomen totdat de fout is verholpen. Zo groeit er geen nieuwe oude last bij, terwijl de bestaande stapsgewijs wordt afgebouwd. Samen met [tests met PHPUnit en Pest](https://phpsos.de/nl/php/php-testen/) vormt ze het fundament van de codekwaliteit.

## PHPStan: niveaus van 0 tot 10

PHPStan wordt als ontwikkelafhankelijkheid geïnstalleerd en heeft om te draaien PHP vanaf 7.4 nodig:

```bash
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse src tests
```

Zonder verdere aanduiding controleert PHPStan alleen het hoogstnoodzakelijke. De strengheid stuurt u via elf niveaus, elk niveau omvat de onderliggende:

| Niveau | Wat er extra wordt gecontroleerd (selectie) |
|---|---|
| 0 | onbekende klassen en functies, onjuist aantal argumenten, altijd ongedefinieerde variabelen |
| 3 | retourtypen en typen die aan eigenschappen worden toegekend |
| 5 | typen van de argumenten bij functie- en methodeaanroepen |
| 6 | ontbrekende typeaanduidingen |
| 8 | methodeaanroepen en eigenschapstoegang op waarden die null kunnen zijn |
| 10 | strengste omgang met `mixed`, ook bij ontbrekende typen |

Met `--level max` draait altijd het hoogste niveau. De documentatie waarschuwt echter dat een upgrade van PHPStan dan veel nieuwe meldingen kan opleveren. Een typische vondst vanaf niveau 8:

```php
<?php
function kundenname(?Kunde $kunde): string
{
    return $kunde->name();   // PHPStan: aanroep op een waarde die null kan zijn
}
```

## PHPStan invoeren zonder foutenvloed: de baseline

In gegroeide projecten meldt PHPStan op een hoog niveau vaak honderden plaatsen. De baseline lost het probleem op: ze slaat alle bestaande meldingen op in een bestand, en PHPStan meldt vanaf dan alleen nog nieuwe fouten.

```bash
vendor/bin/phpstan analyse --level 6 src tests --generate-baseline
```

Het gegenereerde bestand `phpstan-baseline.neon` neemt u op in de configuratie:

```neon
includes:
    - phpstan-baseline.neon

parameters:
    level: 6
    paths:
        - src
        - tests
```

Zo blijft de oude voorraad zichtbaar gedocumenteerd, terwijl nieuwe code vanaf het begin schoon moet zijn.

## Psalm en de taint-analyse

Psalm streeft vergelijkbare doelen na als PHPStan. De niveaus lopen omgekeerd: 1 is het strengst, 8 het toegeeflijkst, de standaard is 2. Psalm heeft PHP vanaf 8.2 nodig en wordt zo ingericht:

```bash
composer require --dev vimeo/psalm
./vendor/bin/psalm --init
./vendor/bin/psalm --taint-analysis
```

De taint-analyse is voor de beveiliging bijzonder interessant. „Taint” betekent besmetting: Psalm markeert gegevens uit bronnen die een aanvaller kan controleren, zoals `$_GET`, `$_POST`, `$_COOKIE`, `$_REQUEST` of `php://input`. Vervolgens volgt het of deze gegevens onbeschermd op gevaarlijke plaatsen aankomen, de zogeheten sinks: uitvoer met `echo`, `include`, `header` of SQL-queries. Tot de ingebouwde categorieën behoren onder meer `sql`, `html`, `shell`, `include`, `eval`, `ssrf` en `unserialize`.

```php
<?php
// Psalm meldt een stroom van $_GET naar de HTML-uitvoer (categorie html)
echo 'Hallo ' . $_GET['name'];

// Verholpen: uitvoer contextgericht escapen
echo 'Hallo ' . htmlspecialchars($_GET['name'] ?? '', ENT_QUOTES, 'UTF-8');
```

De Psalm-documentatie noemt de grenzen openlijk: de analyse gaat ervan uit dat het escapen correct gebeurt. Subtiele fouten, bijvoorbeeld ontbrekende aanhalingstekens rond een HTML-attribuut, kunnen toch lekken laten bestaan. Psalm adviseert daarom geparametriseerde queries voor SQL en contextbewuste templates voor HTML. Resultaten kunnen in het SARIF-formaat worden uitgevoerd, dat onder meer GitHub Code Scanning begrijpt.

## Statische analyse en beveiliging na PHP2Shell

Hier loont een eerlijke afbakening. PHP2Shell, dus CVE-2026-109618 en CVE-2026-109619, zit in de PHP-interpreter. Geen hulpmiddel dat uw toepassingscode leest, vindt deze lekken, en geen codewijziging dicht ze. Dat doet alleen de update naar PHP 8.5.12, 8.4.27, 8.3.36 of 8.2.35.

Toch draagt statische analyse bij aan de beveiliging, op drie punten:

1. **Upgradevaardigheid:** Ze vindt code die op nieuwe PHP-versies niet meer draait. Daarmee daalt de drempel om van 8.1 of 8.2 naar een ondersteunde versie over te stappen.
2. **Eigen lekken:** De taint-analyse vindt routes voor [SQL-injectie](https://phpsos.de/nl/ratgeber/sql-injection-php/), cross-site scripting of bestandsinsluiting. Het PHP-project maakt in zijn beveiligingsbeleid duidelijk dat ongecontroleerde invoer naar functies als `mysqli_query()` geen fout van PHP is, maar onder de verantwoordelijkheid van de toepassing valt.
3. **Robuustheid:** Veel fatale fouten ontstaan door aanroepen op null of onjuiste typen. php.net raadt in verband met CVE-2026-109619 uitdrukkelijk aan om van buitenaf uit te lokken fatale fouten in de toepassing te verhelpen.

Statische analyse vervangt geen [beveiligings-codereview](https://phpsos.de/nl/blog/sicherheits-code-review-php/) door mensen, maar maakt die wel efficiënter, omdat routinefouten vooraf zijn uitgefilterd.

## Hoe helpt PHP SOS concreet?

In het kader van [beveiligingsaudit en penetratietest](https://phpsos.de/nl/leistungen/sicherheitsaudit-penetrationstest/) gaan wij als volgt te werk:

- **Hulpmiddelen inrichten:** PHPStan of Psalm met passend niveau en baseline, ingebed in uw bestaande pipeline.
- **Beveiligingsbevindingen beoordelen:** Wij controleren de resultaten van de taint-analyse en scheiden echte risico's van valse alarmen.
- **Upgrade voorbereiden:** Wij tonen welke plaatsen een overstap naar een ondersteunde PHP-versie blokkeren.
- **Resultaten overdragen:** U ontvangt een geprioriteerde lijst met concrete codeplaatsen voor uw team of bureau.


## Veelgestelde vragen

**Wat is het verschil tussen PHPStan en Psalm?**  
Beide controleren typen en vinden fouten zonder uitvoering. PHPStan telt zijn niveaus van 0 (soepel) tot 10 (streng), Psalm van 8 (soepel) tot 1 (streng). Psalm heeft een ingebouwde taint-analyse voor beveiligingsproblemen.

**Kan ik PHPStan in een oud project invoeren?**  
Ja. Met een baseline slaat PHPStan alle bestaande meldingen op in een bestand en meldt daarna alleen nieuwe fouten. Zo start het team zonder foutenvloed en bouwt het oude lasten stapsgewijs af.

**Vindt statische analyse PHP2Shell?**  
Nee. PHP2Shell zit in de PHP-interpreter, niet in uw code. Daartegen helpt alleen de PHP-update. Statische analyse helpt wel om de code voor te bereiden op nieuwe PHP-versies, zodat de update sneller mogelijk is.

**Welk niveau moet men kiezen?**  
Voor de start een laag niveau zonder baseline of een hoger niveau met baseline. Belangrijker dan het getal is dat de analyse bij elke wijziging automatisch draait en nieuwe fouten niet doorlaat.

## Bronnen

- [PHPStan](https://phpstan.org/)
- [Psalm](https://psalm.dev/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
