Skip to Content

Chapitre 14.5 — PHPStan : l’analyse statique

Partie 14 : Tests & qualité — Chapitre 5/7 Version de référence : PHPStan 2 / Symfony 7.4.

⏱️ TL;DR

  • PHPStan analyse votre code sans l’exécuter et trouve des bugs : types incompatibles, méthodes/propriétés inexistantes, null mal géré, code mort, arguments erronés.
  • Niveaux 0 → 9 (max) : on démarre à un niveau et on monte progressivement. Le niveau max est très strict.
  • Extensions Symfony et Doctrine rendent l’analyse consciente du framework (services, repositories, magie).
  • PHPStan lit les annotations de génériques (@param Booking[], chapitre 2.2) → il vérifie ce que le moteur PHP ne peut pas.
  • Une baseline permet d’adopter PHPStan sur du code existant sans tout corriger d’un coup.
  • Pour un dev Next.js : PHPStan ≈ le compilateur TypeScript (tsc), appliqué à PHP — la sécurité de type que le langage seul ne donne pas entièrement.

🎯 Objectifs

  • Installer et configurer PHPStan.
  • Comprendre les niveaux et monter progressivement.
  • Exploiter les génériques via annotations.
  • Gérer faux positifs et baseline.

1. Ce que PHPStan attrape

L’analyse statique lit le code et raisonne sur les types et les flux, sans exécution. Elle détecte, entre autres :

  • appeler une méthode inexistante ($booking->getFoo() alors que getFoo n’existe pas) ;
  • passer un mauvais type à une fonction ;
  • un accès sur une valeur potentiellement null ($user->getEmail() alors que $user peut être null) ;
  • du code mort (condition toujours vraie/fausse) ;
  • une incohérence entre une annotation @param/@return et l’usage.

Ce sont des bugs qui, sinon, n’apparaîtraient qu’au runtime (voire en prod). PHPStan les remonte avant l’exécution.

2. Installer

composer require --dev phpstan/phpstan phpstan/extension-installer \ phpstan/phpstan-symfony phpstan/phpstan-doctrine

L’extension-installer active automatiquement les extensions Symfony (comprend le conteneur, les routes, la magie du framework) et Doctrine (types des repositories, du QueryBuilder). Sans elles, PHPStan produirait des faux positifs sur le code Symfony.

# phpstan.neon parameters: level: 6 # on démarre à 6, on montera paths: - src - tests symfony: containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml

On lance :

vendor/bin/phpstan analyse

3. Les niveaux

PHPStan a 10 niveaux (0 à 9/max), du plus permissif au plus strict :

NiveauCe qui est vérifié (cumulatif)
0-2erreurs de base (méthodes/fonctions inconnues, arguments)
3-4types de retour, propriétés, null basiques
5-6types d’arguments, génériques (via annotations)
7-8union types stricts, null partout, mixed
9 (max)le plus strict (types mixed traqués)

Stratégie : sur un nouveau projet, visez 8 ou max dès le départ. Sur un existant, démarrez à un niveau atteignable (5-6) et montez d’un cran régulièrement en corrigeant les erreurs. Chaque niveau attrape une nouvelle classe de bugs.

4. Les génériques via annotations

Rappel (chapitre 2.2) : PHP n’a pas de génériques natifs, mais PHPStan lit les annotations et les vérifie :

/** * @param Booking[] $bookings * @return array<string, int> */ public function countByStatus(array $bookings): array { foreach ($bookings as $booking) { $booking->getStatus(); // PHPStan SAIT que $booking est un Booking → vérifie getStatus() } // ... }

Si vous passez un array de Provider à cette méthode, ou appelez une méthode inexistante sur $booking, PHPStan le signale. C’est ainsi qu’on récupère l’essentiel de la sécurité des génériques en PHP — via un outil, pas le langage.

5. Faux positifs et baseline

Parfois PHPStan se trompe (magie non comprise) ou vous ne pouvez pas corriger tout de suite. Deux mécanismes :

  • Ignorer une ligne précise :
    /** @phpstan-ignore-next-line */ $x = $magicThing->whatever();
    (à utiliser avec parcimonie, et en justifiant).
  • Baseline : geler les erreurs existantes pour ne bloquer que les nouvelles :
    vendor/bin/phpstan analyse --generate-baseline
    Le fichier phpstan-baseline.neon liste les erreurs tolérées ; le code neuf doit être propre. C’est la façon d’adopter PHPStan sur un projet existant sans tout réécrire.

⚠️ Piège : abuser de @phpstan-ignore ou d’une baseline énorme vide l’outil de son intérêt. La baseline est une dette à résorber progressivement, pas un tapis sous lequel cacher les erreurs. Traitez chaque ignore comme un TODO. L’objectif : un code qui passe sans exceptions.

💡 Pour un dev Next.js : PHPStan est votre tsc --strict pour PHP. Le niveau PHPStan ≈ la rigueur de tsconfig. Comme en TS, l’idéal est de viser le niveau strict sur le code neuf et de monter progressivement sur l’existant. Combiné aux types de bout en bout (OpenAPI → TS, Partie 11.8), vous obtenez une chaîne typée du backend PHP au front Next.js.

✏️ Exercices

Exercice 1. PHPStan signale « Cannot call method getEmail() on App\Entity\User|null ». Que révèle cette erreur et comment la corriger ?

✅ Solution

Elle révèle que la variable peut être null (ex. $user = $repo->find($id) renvoie User|null) et qu’on appelle getEmail() sans vérifier — un bug potentiel (TypeError/Error au runtime si null). Corrections : garder l’appel derrière un test (if ($user !== null) { ... }), utiliser le nullsafe $user?->getEmail(), ou lever si absent ($user ?? throw new NotFoundHttpException()). PHPStan force à traiter le cas null.

Exercice 2. Quelle stratégie de niveau adopter sur un projet neuf vs existant ?

✅ Solution

Neuf : viser un niveau élevé (8 ou max) dès le départ — le code naît propre, aucune dette. Existant : démarrer à un niveau atteignable (5-6) et monter d’un cran régulièrement en corrigeant les erreurs à chaque palier ; éventuellement figer les erreurs actuelles avec une baseline pour ne bloquer que le code neuf, et résorber la baseline au fil du temps.

Exercice 3. À quoi sert une baseline PHPStan, et quel est son danger ?

✅ Solution

Elle fige les erreurs existantes (les tolère) pour que seuls les nouveaux problèmes bloquent — permet d’adopter PHPStan sur du legacy sans tout corriger d’un coup. Danger : une baseline énorme qu’on ne résorbe jamais vide l’outil de son sens (on cache les erreurs au lieu de les corriger). La baseline doit être une dette à réduire progressivement, pas un dépotoir permanent.

🧠 Quiz de révision

1. Que fait PHPStan, et quand attrape-t-il les bugs ?

Il analyse le code sans l’exécuter (analyse statique) et attrape des bugs (types, null, méthodes inexistantes) avant le runtime.

2. Comment gère-t-on les génériques absents de PHP ?

Via des annotations (@param Booking[], @return array<string,int>) que PHPStan vérifie — l’équivalent des génériques, par l’outil.

3. Quelle stratégie de niveau sur un projet existant ?

Démarrer à un niveau atteignable puis monter progressivement, éventuellement avec une baseline pour ne bloquer que le code neuf.

4. À quoi ressemble PHPStan pour un dev TypeScript ?

À tsc --strict : la vérification de types statique que le langage seul n’offre pas entièrement.

5. Quel danger avec les @phpstan-ignore et la baseline ?

En abuser vide l’outil de son intérêt : ce sont des dettes à résorber, pas des moyens de cacher durablement les erreurs.


Prochain chapitre : 14.6 — CS Fixer & Rector — style cohérent et refactoring automatisé.