> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Statische analyse van packages (PHPStan / Larastan)

> Setup, configuratie, typeannotaties en automatische uitvoering in CI om met PHPStan en Larastan de typeveiligheid en onderhoudbaarheid van Laravel-packages te verhogen.

Een Laravel-package is niet klaar na één release. Om het jarenlang te onderhouden terwijl je meebeweegt met updates van Laravel zelf, PHP en je dependencies, moet je naast tests ook continu statische analyse draaien.

<Info>
  Deze pagina is een aanvullende gids bij [Laravel-packages ontwikkelen](/nl/advanced/package-development), [Laravel-packages testen met Orchestra Testbench](/nl/advanced/package-testing) en [Versiecompatibiliteit van packages beheren](/nl/advanced/package-versioning). Als je statische analyse toevoegt aan implementatie, tests en versiestrategie, krijg je een package dat op de lange termijn goed te onderhouden is.
</Info>

## Wat is statische analyse?

Statische analyse is een techniek die type-inconsistenties en potentiële bugs opspoort zonder de code uit te voeren. Omdat Laravel-packages veel dynamische mechanismen bevatten — de service container, facades, Eloquent — vang je hiermee in een vroeg stadium problemen op die je bij alleen functioneel testen makkelijk mist.

Statische analyse invoeren levert vooral het volgende op:

* **Betere typeveiligheid** — verkeerde argumenten of returnwaarden worden vóór de review gedetecteerd
* **Vroege bugdetectie** — aanroepen van niet-bestaande methodes of over het hoofd geziene nullables ontdek je vóór het testen
* **Betere IDE-autocomplete** — door PHPDoc en generics op orde te brengen wordt de autocomplete nauwkeuriger
* **Stabieler langetermijnonderhoud** — bij upgrades van Laravel of PHP spoor je kapotte plekken makkelijker op

## PHPStan opzetten

Zet eerst met alleen PHPStan een analysefundament op. PHPStan 2.x gaat strikter om met `mixed` en nullables dan voorheen, dus het is ook een goed moment om de publieke API van je package op te schonen.

<Steps>
  <Step title="Voeg PHPStan toe als dev-dependency">
    ```bash theme={null}
    composer require --dev phpstan/phpstan:^2.0
    ```
  </Step>

  <Step title="Analyseer eerst de doelmappen rechtstreeks">
    Bij packages neem je meestal `src` en `tests` als eerste doelwit.

    ```bash theme={null}
    vendor/bin/phpstan analyse src tests
    ```
  </Step>

  <Step title="Leg het vast als Composer-script">
    Door een script te definiëren in `composer.json` gebruik je in CI en lokaal hetzelfde commando, wat het beheer makkelijker maakt.

    ```json theme={null}
    {
        "scripts": {
            "analyse": "phpstan analyse"
        }
    }
    ```
  </Step>
</Steps>

## Larastan opzetten

Met alleen PHPStan begrijpt de analyse Laravel-specifieke mechanismen zoals containerresolutie, facades en Eloquent-relaties onvoldoende. Daarom gebruik je daarnaast Larastan, de Laravel-extensie voor PHPStan.

<Steps>
  <Step title="Voeg Larastan toe">
    De huidige packagenaam is `larastan/larastan`. In oudere artikelen kom je soms `nunomaduro/larastan` tegen.

    ```bash theme={null}
    composer require --dev larastan/larastan:^3.0
    ```
  </Step>

  <Step title="Installeer bij packageontwikkeling ook Testbench">
    Larastan start een Laravel-applicatiecontainer om types op te lossen. Als je een Laravel-package los analyseert, kan `orchestra/testbench` nodig zijn.

    ```bash theme={null}
    composer require --dev orchestra/testbench
    ```
  </Step>

  <Step title="Laad extension.neon in">
    Include de Larastan-configuratie in `phpstan.neon`.

    ```neon theme={null}
    includes:
        - vendor/larastan/larastan/extension.neon
    ```
  </Step>
</Steps>

## De configuratie van phpstan.neon

`phpstan.neon` is de centrale configuratie voor het analyseniveau, de doelpaden, uitgesloten paden en uitzonderingsregels. Voor een Laravel-package is het realistischer om een configuratie te kiezen die je stapsgewijs kunt aanscherpen dan om meteen op een perfecte score te mikken.

Hieronder een voorbeeld van `phpstan.neon`.

```neon theme={null}
includes:
    - vendor/larastan/larastan/extension.neon

parameters:
    level: 5

    paths:
        - src
        - tests

    excludePaths:
        analyse:
            - vendor
            - workbench
            - bootstrap/cache/*

    ignoreErrors:
        -
            message: '#Call to an undefined method Illuminate\\Support\\HigherOrderCollectionProxy::#'
            path: tests/Fixtures/*
```

### Niveau 0–9 kiezen

PHPStan kun je stapsgewijs invoeren. Het is het veiligst om te beginnen op een niveau dat je huidige codebase aankan en het te verhogen naarmate je verbeteringen doorvoert.

| Niveau | Geschikte fase                                                                                     |
| ------ | -------------------------------------------------------------------------------------------------- |
| 0–3    | De fase waarin je eerst onbekende klassen, functies en methodeaanroepen wilt terugdringen          |
| 4–6    | De fase waarin je de argument-, return- en property-types van de publieke API op orde wilt brengen |
| 7–9    | De langetermijnonderhoudsfase waarin je nullables, unions en `mixed` strikt wilt behandelen        |

<Tip>
  PHPStan 2.x heeft ook level 10, maar voor Laravel-packages is het realistisch om eerst 5–7 te stabiliseren en dan door te groeien naar 8–9. Bij een nieuw package kun je beter meteen op een hoger niveau beginnen; dat scheelt terugwerken.
</Tip>

### `paths`

In `paths` geef je expliciet de mappen op die je wilt analyseren. Bij packages loont het om naast `src` ook `tests` op te nemen, waar types makkelijk verwateren.

### `excludePaths`

Sluit alleen plekken uit waar statische analyse weinig waarde heeft, zoals gegenereerde bestanden, caches en de Workbench voor verificatie. Als je te breed uitsluit, zie je ook de fouten niet meer die je juist wilde detecteren.

### `ignoreErrors`

`ignoreErrors` is een laatste redmiddel. Beperk de melding met een reguliere expressie en gebruik ook `path` om de reikwijdte in te perken. Zo kun je makkelijk terugdraaien wanneer Laravel of Larastan in de toekomst verbetert.

## Veelgebruikte typeannotaties

De nauwkeurigheid van PHPStan en Larastan hangt sterk af van hoe je PHPDoc schrijft. Bij Laravel-packages is het vooral waardevol om `@param`, `@return`, `@var` en generics (`@template`) op orde te hebben.

```php theme={null}
<?php

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;

/**
 * @template TModel of Model
 */
final class ModelRepository
{
    /**
     * @param class-string<TModel> $modelClass
     */
    public function __construct(private string $modelClass)
    {
    }

    /**
     * @return TModel|null
     */
    public function find(int $id): ?Model
    {
        return $this->modelClass::query()->find($id);
    }

    /**
     * @return Collection<int, TModel>
     */
    public function all(): Collection
    {
        /** @var Collection<int, TModel> $models */
        $models = $this->modelClass::query()->get();

        return $models;
    }
}
```

In dit voorbeeld geef je de volgende informatie aan PHPStan door:

* `@param class-string<TModel>` — geeft aan dat de string geen willekeurige string is, maar de naam van een Model-klasse
* `@return TModel|null` — vertelt dat `find()` een concreet modeltype teruggeeft
* `@var Collection<int, TModel>` — maakt de key/value-types van de collection expliciet
* `@template TModel of Model` — drukt een herbruikbare generieke repository uit

## Laravel-specifieke aandachtspunten

De "handige magic" van Laravel komt niet vanzelf bij de statische analyse terecht. Je moet aan de packagekant type-informatie toevoegen en het in een vorm gieten die de analyzer begrijpt.

### Facades

Bij een custom facade helpt het om de methodes die gebruikers aanroepen in PHPDoc aan te vullen; dat werkt zowel voor de IDE als voor statische analyse.

```php theme={null}
<?php

namespace Vendor\Package\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * @method static string issueToken(int $userId)
 */
class Tokenizer extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return 'package.tokenizer';
    }
}
```

### Magic methods

API's die leunen op `__call()` of Macroable zijn handig, maar het zijn plekken waar types makkelijk verwateren. Als je er een publieke API van maakt, is het veiliger om wrapper-methodes toe te voegen waarvan de argumenten en returnwaarden duidelijk zijn.

<Warning>
  Als je alleen omwille van de statische analyse steeds meer `ignoreErrors` toevoegt, verberg je hoe facades en macro's echt kapotgaan. Kijk eerst of je het kunt oplossen met expliciete methodes, getypte value objects of extra PHPDoc.
</Warning>

### Types opgeven voor Eloquent-modellen

Voor Eloquent-relaties en dynamische properties werkt de combinatie van `@property` en generics op relaties goed.

```php theme={null}
<?php

namespace Vendor\Package\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
 * @property-read Team $team
 */
class Member extends Model
{
    /**
     * @return BelongsTo<Team, self>
     */
    public function team(): BelongsTo
    {
        return $this->belongsTo(Team::class);
    }
}
```

## Automatisch uitvoeren in CI

Voer statische analyse niet alleen lokaal uit, maar altijd ook in CI. Zeker bij packages die meerdere Laravel-versies ondersteunen kun je, met dezelfde aanpak als de testmatrix in [Versiecompatibiliteit van packages beheren](/nl/advanced/package-versioning), compatibiliteit en typeveiligheid tegelijk bewaken.

Hieronder een voorbeeld van `.github/workflows/static-analysis.yml`.

```yaml theme={null}
name: Static analysis

on:
  push:
  pull_request:

jobs:
  static-analysis:
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        php: [8.2, 8.3, 8.4]
        laravel: ["^12.0", "^13.0"]
        include:
          - laravel: "^13.0"
            testbench: "^10.0"
          - laravel: "^12.0"
            testbench: "^9.0"

    name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - PHPStan

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          extensions: dom, curl, libxml, mbstring, zip
          coverage: none

      - name: Install dependencies
        run: |
          composer require "laravel/framework:${{ matrix.laravel }}" \
                           "orchestra/testbench:${{ matrix.testbench }}" \
                           --no-interaction --no-update
          composer update --prefer-dist --no-interaction

      - name: Run static analysis
        run: vendor/bin/phpstan analyse --error-format=github
```

In dit voorbeeld gebruik je dezelfde Laravel/Testbench-combinatietabel als in je testmatrix. Als je met zowel tests als statische analyse dezelfde combinaties bewaakt, mis je minder snel de situatie waarin "de code wel draait, maar de types kapot zijn".

## Veelvoorkomende false positives en hoe je ermee omgaat

Vraag je bij een waarschuwing van de statische analyse eerst af of er type-informatie ontbreekt. Wat op een false positive lijkt, is in werkelijkheid vaak gewoon een tekort aan PHPDoc.

### `@phpstan-ignore-next-line`

Bruikbaar als tijdelijke omweg, maar beperk het tot regels waarvan je zelf de reden kunt uitleggen.

```php theme={null}
// @phpstan-ignore-next-line Omdat de macro van Laravel pas tijdens runtime wordt geregistreerd
$builder->whereLike('name', $keyword);
```

### `ignoreErrors`

Alleen overwegen wanneer dezelfde fout op meerdere plekken opduikt. Beperk altijd het pad en smoor niet met een slordige reguliere expressie alles in de kiem.

```neon theme={null}
parameters:
    ignoreErrors:
        -
            message: '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder::whereLike\(\)#'
            path: tests/Fixtures/*
```

### Aanpak die je eerst moet proberen

1. PHPDoc toevoegen
2. Returntypes van facades en relaties expliciet maken
3. `mixed` vervangen door concrete types
4. Alleen de false positives negeren die je kunt verantwoorden

## Gerelateerde pagina's

<Columns cols={3}>
  <Card title="Laravel-packages ontwikkelen" icon="box" href="/nl/advanced/package-development">
    Bekijk de basis van een package-implementatie, inclusief service providers en gepubliceerde resources.
  </Card>

  <Card title="Laravel-packages testen met Orchestra Testbench" icon="flask-conical" href="/nl/advanced/package-testing">
    Uitleg over de testinfrastructuur voor packages die je samen met statische analyse wilt gebruiken.
  </Card>

  <Card title="Versiecompatibiliteit van packages beheren" icon="git-branch" href="/nl/advanced/package-versioning">
    Bekijk de Laravel/PHP-compatibiliteitstabel en de matrixstrategie voor GitHub Actions.
  </Card>
</Columns>


## Related topics

- [Collection deep dive](/nl/advanced/collection-deep-dive.md)
- [Laravel Package Skeleton — officiële startertemplate voor packages](/nl/blog/package-skeleton-introduction.md)
- [Het nieuwe code-analyse-ecosysteem van Laravel — surveyor / ranger / roster](/nl/blog/laravel-ecosystem-analysis.md)
- [Aan de slag met Laravel-testen in Pest PHP](/nl/blog/pest-introduction.md)
- [Laravel PAO — outputoptimalisatie voor AI-agents](/nl/blog/pao-introduction.md)
