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,
nullmal 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 quegetFoon’existe pas) ; - passer un mauvais type à une fonction ;
- un accès sur une valeur potentiellement
null($user->getEmail()alors que$userpeut êtrenull) ; - du code mort (condition toujours vraie/fausse) ;
- une incohérence entre une annotation
@param/@returnet 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-doctrineL’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.xmlOn lance :
vendor/bin/phpstan analyse3. Les niveaux
PHPStan a 10 niveaux (0 à 9/max), du plus permissif au plus strict :
| Niveau | Ce qui est vérifié (cumulatif) |
|---|---|
| 0-2 | erreurs de base (méthodes/fonctions inconnues, arguments) |
| 3-4 | types de retour, propriétés, null basiques |
| 5-6 | types d’arguments, génériques (via annotations) |
| 7-8 | union 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 :
(à utiliser avec parcimonie, et en justifiant).
/** @phpstan-ignore-next-line */ $x = $magicThing->whatever(); - Baseline : geler les erreurs existantes pour ne bloquer que les nouvelles :
Le fichier
vendor/bin/phpstan analyse --generate-baselinephpstan-baseline.neonliste 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-ignoreou 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 chaqueignorecomme un TODO. L’objectif : un code qui passe sans exceptions.
💡 Pour un dev Next.js : PHPStan est votre
tsc --strictpour PHP. Le niveau PHPStan ≈ la rigueur detsconfig. 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 ?
@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é.