Skip to content

Résoudre erreurs PHPStan niveau 9 ★★★ Avancé ~15 min ​

Contexte ​

PHPStan niveau 9 est le niveau d'analyse statique le plus strict pour PHP. Il détecte des erreurs subtiles (types, generics, array shapes, nullabilité) mais les corriger manuellement est fastidieux et source d'erreurs.

Objectif ​

Corriger automatiquement les erreurs PHPStan niveau 9 avec :

  • ✅ Analyse complète du projet
  • ✅ Auto-fix automatique des erreurs courantes
  • ✅ Loop jusqu'à 0 erreur ou max iterations
  • ✅ Préservation de la logique métier
  • ✅ Tests de non-régression

Prérequis ​

Plugins :

  • qa - PHPStan resolver

Outils :

  • PHPStan ≥ 1.10 installé
  • Configuration phpstan.neon niveau 9
  • Tests PHPUnit

Configuration :

phpstan.neon :

yaml
parameters:
    level: 9
    paths:
        - src
        - tests
    ignoreErrors: []

Workflow Étape par Étape ​

Phase 1 : Analyser et corriger automatiquement ​

Commande :

bash
/qa:phpstan-resolver

Que se passe-t-il ?

Claude lance un loop d'auto-fix :

  1. Analyse - Lance vendor/bin/phpstan analyse --level=9
  2. Regroupement - Groupe erreurs par type
  3. Auto-fix - Applique corrections automatiques
  4. Validation - Relance PHPStan pour vérifier
  5. Loop - Répète jusqu'à 0 erreur ou 10 itérations max
  6. Tests - Lance tests pour vérifier non-régression

Output attendu :

🔍 PHPStan Resolver - Analyse niveau 9

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Itération 1/10
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📊 Erreurs détectées : 28

Regroupement par type :
- Missing type hints : 12 erreurs
- Nullable without check : 8 erreurs
- Array shape violations : 5 erreurs
- Generic type missing : 3 erreurs

🔧 Auto-fixes appliqués : 20/28

Corrections :
✅ UserRepository.php:42 - Ajout type hint
✅ OrderService.php:67 - Null check ajouté
✅ ProductController.php:24 - Array shape annotation
... (17 autres)

⚠️  Manuel requis : 8 erreurs complexes

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Itération 2/10
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📊 Erreurs restantes : 8

🔧 Auto-fixes appliqués : 5/8

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Itération 3/10
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📊 Erreurs restantes : 3

⚠️  Erreurs complexes (intervention manuelle) :

1. UserRepository.php:125
   Error: Method return type has no value type specified in iterable type array

   Code actuel :
   public function findActive(): array

   Suggestion :
   /**
    * @return array<User>
    */
   public function findActive(): array

2. OrderService.php:89
   Error: Property OrderService::$cache has generic class Cache but does not specify its types

   Code actuel :
   private Cache $cache;

   Suggestion :
   /** @var Cache<string, Order> */
   private Cache $cache;

3. ProductController.php:156
   Error: Parameter #1 $data of method save() expects array{name: string, price: float}, array given

   Code actuel :
   $this->service->save($request->toArray());

   Suggestion : Valider shape avec assert ou créer DTO

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 RÉSULTAT FINAL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Erreurs initiales : 28
Erreurs corrigées : 25 (89%)
Erreurs restantes : 3 (requièrent intervention manuelle)

✅ Tests : All passing (142 tests)
✅ Pas de régression détectée

Fichiers modifiés : 18
Corrections appliquées : 25

Rapport détaillé : docs/specs/phpstan-resolution-2026-02-01.md

Exemple Complet ​

Scénario : Projet avec 42 erreurs PHPStan niveau 9 ​

Erreurs initiales :

bash
$ vendor/bin/phpstan analyse --level=9
 42/42 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ -------------------------------------------------------------------
  Line   src/Repository/UserRepository.php
 ------ -------------------------------------------------------------------
  42     Method findByEmail() has no return type specified
  67     Method findActive() return type has no value type in iterable
  89     Parameter $id has no type specified
  125    Method save() has parameter $user with generic type but no types
 ------ -------------------------------------------------------------------

 ------ -------------------------------------------------------------------
  Line   src/Service/OrderService.php
 ------ -------------------------------------------------------------------
  24     Property $repository has no type specified
  56     Method calculate() has no return type specified
  78     Parameter #1 expects Order|null, Order given (missing null check)
  102    Property $cache has generic class but does not specify types
 ------ -------------------------------------------------------------------

... (34 autres erreurs)

[ERROR] Found 42 errors

Commande :

bash
/qa:phpstan-resolver

Corrections automatiques appliquées :

1. Type hints manquants (15 erreurs) ​

Avant :

php
public function findByEmail($email)
{
    return $this->repository->findOneBy(['email' => $email]);
}

Après :

php
public function findByEmail(string $email): ?User
{
    return $this->repository->findOneBy(['email' => $email]);
}

2. Generics manquants (8 erreurs) ​

Avant :

php
public function findActive(): array
{
    return $this->repository->findBy(['active' => true]);
}

Après :

php
/**
 * @return array<User>
 */
public function findActive(): array
{
    return $this->repository->findBy(['active' => true]);
}

3. Null checks manquants (12 erreurs) ​

Avant :

php
public function process(int $orderId): void
{
    $order = $this->repository->find($orderId);
    $this->validator->validate($order); // Crash si null
}

Après :

php
public function process(int $orderId): void
{
    $order = $this->repository->find($orderId);

    if (!$order) {
        throw new OrderNotFoundException($orderId);
    }

    $this->validator->validate($order);
}

4. Array shapes (5 erreurs) ​

Avant :

php
public function create(array $data): Product
{
    return new Product(
        $data['name'],
        $data['price']
    );
}

Après :

php
/**
 * @param array{name: string, price: float, active?: bool} $data
 */
public function create(array $data): Product
{
    return new Product(
        $data['name'],
        $data['price'],
        $data['active'] ?? true
    );
}

5. Collections Doctrine (2 erreurs) ​

Avant :

php
class Order
{
    private Collection $items;
}

Après :

php
class Order
{
    /** @var Collection<int, OrderItem> */
    private Collection $items;
}

Résultat final :

bash
$ vendor/bin/phpstan analyse --level=9
 42/42 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 [OK] No errors

$ vendor/bin/phpunit
OK (142 tests, 487 assertions)

Variantes ​

Analyse d'un seul fichier ​

bash
/qa:phpstan-resolver --path=src/Service/OrderService.php

Mode dry-run ​

bash
/qa:phpstan-resolver --dry-run

Affiche les corrections qui seraient appliquées sans les appliquer.

Avec baseline ​

bash
/qa:phpstan-resolver --generate-baseline

Génère une baseline pour ignorer temporairement les erreurs non critiques.

Troubleshooting ​

Erreurs qui reviennent après correction ​

Symptôme : Erreur corrigée à l'itération 2 mais réapparaît à l'itération 3

Solution : Conflit entre corrections. Vérifier manuellement le fichier concerné.

Tests en échec après corrections ​

Symptôme : Tests: 3 failures after PHPStan fixes

Solution :

  1. Analyser les erreurs de test
  2. Les corrections ont peut-être changé le comportement
  3. Ajuster les tests ou les corrections

Max iterations atteint ​

Symptôme : Max iterations (10) reached, still 5 errors

Solution :

Erreurs trop complexes pour auto-fix. Corriger manuellement :

bash
vendor/bin/phpstan analyse --error-format=table

Erreur "PHPStan not found" ​

Symptôme : Command 'phpstan' not found

Solution :

bash
composer require --dev phpstan/phpstan

Liens Connexes ​

Use cases :

Plugins :

Documentation :

Tips & Best Practices ​

✅ Bonnes pratiques ​

  • Niveau progressif : commencer niveau 5, puis 7, puis 9
  • Baseline : utiliser baseline pour legacy code
  • Tests : toujours lancer tests après corrections
  • CI/CD : ajouter PHPStan en CI pour prévenir régressions

🔍 Optimisations ​

PHPStan config optimale :

yaml
# phpstan.neon
parameters:
    level: 9
    paths:
        - src
        - tests

    # Extensions utiles
    symfony:
        container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml

    doctrine:
        repositoryClass: App\Repository\BaseRepository

    # Ignorer vendor
    excludePaths:
        - vendor
        - var

    # Strictness
    checkMissingIterableValueType: true
    checkGenericClassInNonGenericObjectType: true

Pre-commit hook :

bash
#!/bin/bash
# .git/hooks/pre-commit

vendor/bin/phpstan analyse --level=9 --no-progress

if [ $? -ne 0 ]; then
    echo "❌ PHPStan failed"
    echo "Run: /qa:phpstan-resolver"
    exit 1
fi

CI/CD GitHub Actions :

yaml
name: PHPStan
on: [push, pull_request]

jobs:
  phpstan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: php-actions/composer@v6
      - name: PHPStan
        run: vendor/bin/phpstan analyse --level=9 --error-format=github

🎯 Métriques de qualité ​

Un code PHPStan niveau 9 compliant c'est :

  • ✅ 0 erreur PHPStan
  • ✅ Tous les types explicites
  • ✅ Generics sur collections
  • ✅ Array shapes documentés
  • ✅ Null checks systématiques

📊 Types d'erreurs courants ​

Fréquence des erreurs :

  • Type hints manquants : 35%
  • Generics manquants : 25%
  • Null checks manquants : 20%
  • Array shapes : 10%
  • Autres : 10%

Temps de résolution moyen :

  • 10 erreurs : ~2 min
  • 50 erreurs : ~10 min
  • 100 erreurs : ~20 min
  • 500+ erreurs : utiliser baseline + résolution progressive

Checklist Validation ​

Avant de lancer :

  • [ ] PHPStan installé
  • [ ] phpstan.neon configuré niveau 9
  • [ ] Tests écrits et passent
  • [ ] Commit récent (pour rollback si besoin)

Pendant la résolution :

  • [ ] Loop d'auto-fix lancé
  • [ ] Corrections appliquées progressivement
  • [ ] Tests lancés après chaque itération
  • [ ] Pas de régression détectée

Après résolution :

  • [ ] PHPStan niveau 9 : 0 erreurs
  • [ ] Tests passent
  • [ ] Pas de régression
  • [ ] Commit des corrections
  • [ ] CI/CD vert

Publié sous licence MIT