PHPStan 2.0 : niveau 10 et nouvelles fonctionnalités
Par Louis-Arnaud Catoire
Mis à jour le

PHPStan 2.0 marque un tournant dans l'analyse statique PHP. Au-delà des gains de performance et du nouveau niveau 10, cette version transforme la manière dont on conçoit, documente et sécurise une base de code. Que vous cherchiez à migrer depuis la 1.x ou à comprendre comment le système de types peut porter vos décisions d'architecture, cet article couvre l'essentiel. Pour aller directement dans le concret, les 10 erreurs PHPStan niveau max sur un projet Symfony illustrent les cas les plus fréquents.
Ce que le niveau 10 change concrètement
Le niveau 9 détectait déjà les incohérences de typage, les propriétés non initialisées et les conditions redondantes. Le niveau 10 s'attaque à un angle mort majeur : le type mixed implicite. Un paramètre sans type natif ni PHPDoc est implicitement mixed, et PHP autorise n'importe quelle opération dessus sans broncher. Le niveau 10 refuse ce flou.
Prenons un cas courant. Au niveau 9, ce code ne lève aucune erreur :
function processData($data)
{
return $data->getName();
}
Au niveau 10, PHPStan signale que $data est implicitement mixed. L'appel à getName() devient une erreur, car rien ne garantit que $data est un objet disposant de cette méthode. La correction est directe :
function processData(User $data): string
{
return $data->getName();
}
Ce même principe s'applique aux opérations arithmétiques. Ce code passe au niveau 9 :
function calculateTotal(array $items): int
{
$total = 0;
foreach ($items as $item) {
$total += $item['price'] * $item['quantity'];
}
return $total;
}
Au niveau 10, $item est mixed, donc l'accès à price et quantity est interdit. La solution passe par un array shape :
/**
* @param array<array{price: int, quantity: int}> $items
*/
function calculateTotal(array $items): int
{
$total = 0;
foreach ($items as $item) {
$total += $item['price'] * $item['quantity'];
}
return $total;
}
Ce n'est pas une simple annotation cosmétique. C'est un contrat vérifié statiquement : tout appelant qui passe un tableau dont la structure ne correspond pas sera détecté avant l'exécution.
Migrer de PHPStan 1.x à 2.0
Dépendances et extensions
La mise à jour se lance classiquement :
composer require --dev phpstan/phpstan:^2.0
Les extensions tierces (phpstan-symfony, phpstan-doctrine, phpstan-phpunit) doivent être mises à jour vers leurs versions compatibles 2.0. Le dépôt phpstan-symfony sur GitHub recense les intégrations disponibles. Vérifiez chaque extension avant de lancer la migration : une extension incompatible provoque des erreurs silencieuses ou des faux négatifs.
Redistribution des règles par niveau
Certaines règles ont changé de niveau. Des erreurs qui n'apparaissaient qu'au niveau 8 remontent désormais au niveau 7. Si vous maintenez un niveau fixe dans votre CI, relancez une analyse complète après la migration.
Stratégie baseline pour les projets existants
Sur un projet legacy de plusieurs centaines de milliers de lignes, activer le niveau 10 d'un coup produit des milliers d'erreurs. La baseline est l'outil de transition adapté :
phpstan analyse --generate-baseline
Cette commande génère un fichier phpstan-baseline.neon qui recense toutes les erreurs existantes. PHPStan ne les signale plus dans les analyses suivantes, mais toute nouvelle erreur est détectée immédiatement. La stratégie recommandée consiste à réduire cette baseline progressivement, module par module, en commençant par les couches les plus critiques (domaine métier, services partagés). Pour accélérer ce travail de refactoring, Rector automatise les transformations répétitives et s'intègre naturellement dans ce processus.
Les options de configuration dépréciées depuis la 1.x sont supprimées. Si votre phpstan.neon utilise des clés obsolètes, PHPStan affichera un message clair indiquant la syntaxe de remplacement.
Inférence de types et conditions always-true/always-false
PHPStan 2.0 améliore significativement son moteur d'inférence. Il détecte avec plus de précision les conditions qui sont toujours vraies ou toujours fausses, signe fréquent d'un bug logique ou d'un code mort.
Considérons ce fragment :
function handle(string $status): void
{
if ($status === 'active') {
// ...
}
if ($status === 'active') {
// ...
}
}
PHPStan ne signalera pas ces deux blocs comme redondants (le flow est linéaire). Mais dans un cas comme celui-ci :
function process(int $value): void
{
if ($value > 0) {
if ($value >= 0) {
// always true
}
}
}
La condition interne est toujours vraie. PHPStan la détecte et la signale. Ce type d'alerte révèle des branches mortes, des vérifications redondantes ou des incompréhensions du flow de données. Cette capacité à détecter le code mort est un levier puissant de nettoyage. Sur un projet mature, corriger ces alertes simplifie le code et réduit la surface cognitive pour les développeurs qui le maintiennent.
Besoin d'accompagnement sur votre projet ?
Parlons-enCohérence des annotations @var
PHPStan 2.0 ne fait plus confiance aveuglément aux annotations @var. Quand le type déclaré diverge du type inféré, une erreur est remontée :
/** @var string $count */
$count = $repository->count([]);
count() retourne un int, mais l'annotation déclare un string. En 1.x, l'annotation l'emportait, masquant potentiellement un bug. En 2.0, l'incohérence est signalée. Ce changement encourage à supprimer les @var superflus et à ne les utiliser que lorsque PHPStan ne peut pas inférer le type lui-même (résultats de requêtes dynamiques, désérialisation).
Le type list et la précision des structures de données
PHPStan 2.0 distingue array<int, T> de list<T>. Un list garantit des clés entières consécutives commençant à 0, ce que array_values() produit toujours mais qu'un tableau arbitraire ne garantit pas :
/**
* @param list<string> $names
*/
function displayNames(array $names): void
{
foreach ($names as $index => $name) {
echo "$index: $name\n";
}
}
$users = [3 => 'Alice', 7 => 'Bob'];
displayNames($users);
PHPStan détecte que $users n'est pas une list<string>. Cette distinction semble mineure, mais elle élimine une catégorie de bugs subtils liés aux clés de tableaux après des opérations comme array_filter() sans array_values().
Fonctions pures et effets de bord
L'annotation @phpstan-pure permet de déclarer qu'une fonction ne produit aucun effet de bord. PHPStan 2.0 vérifie cette promesse statiquement :
/** @phpstan-pure */
function formatPrice(int $cents): string
{
return number_format($cents / 100, 2, ',', ' ') . ' €';
}
Cette fonction est pure : même entrée, même sortie, aucune modification d'état. En revanche, ajouter un appel à un logger la rend impure, et PHPStan le signale immédiatement. Marquer explicitement les fonctions pures dans les couches domaine et calcul renforce la prévisibilité et facilite le test unitaire.
Performance : mémoire et temps d'analyse
Ces gains de performance sont d'autant plus appréciables lors d'un audit de code PHP sur de gros projets. PHPStan 2.0 réduit la consommation mémoire de 50 à 70 % grâce à une refonte des structures AST internes et à l'élimination des références circulaires qui freinaient le ramasse-miettes. L'analyse de PrestaShop 8.0 passe de 9 à 3 minutes. Le cache a été refondu : fichiers plus compacts, chargement plus rapide, répertoire de cache réduit de plusieurs centaines de mégaoctets à quelques dizaines. En CI, la restauration et la génération du cache sont sensiblement plus rapides.
Le système de types comme outil d'architecture
Au-delà de la détection de bugs, le niveau 10 transforme PHPStan en outil d'architecture. Quand chaque paramètre, chaque retour de méthode et chaque structure de données est typé avec précision, le code devient auto-documenté. Les array shapes décrivent des contrats de données que le compilateur vérifie. Les types union (string|int) et intersection (Countable&Iterator) expriment des contraintes que seuls des tests d'intégration pouvaient couvrir auparavant.
Cette complétude du système de types a des conséquences architecturales directes. Un service dont toutes les méthodes publiques sont typées au niveau 10 n'a plus besoin de validation défensive en entrée : PHPStan garantit que les appelants respectent le contrat. Les invariants métier exprimés par des types (un PositiveInt via @phpstan-type, un non-empty-string pour un identifiant) sont vérifiés à chaque appel, sans code de garde.
PHPStan devient alors une forme de documentation vivante. Contrairement à un commentaire ou un wiki, un type PHPStan est vérifié à chaque exécution de la CI. Il ne peut pas dériver du code réel. Pour un architecte, c'est un levier puissant : les décisions de design (séparation des couches, contrats entre modules, structures de données partagées) sont encodées dans les types et appliquées automatiquement.
Imposer le niveau 10 sur l'ensemble d'un projet revient à dire : chaque interaction entre composants est explicite et vérifiée. Combiné à des conventions de codage strictes, c'est un investissement initial significatif, mais le retour se mesure en bugs évités, en onboarding accéléré et en refactorings sécurisés. Pour évaluer la maturité de votre codebase avant cette montée en niveau, un audit de code PHP permet de cartographier les zones critiques.
Pour aller plus loin
- Comment PHPStan peut améliorer la qualité de votre code PHP, guide d'introduction
- Blog officiel PHPStan : annonce de la version 2.0, détails complets de la release
- PHPStan sur GitHub, code source et issues
- PHP 8.4 : nouvelles fonctionnalités, la dernière version PHP au moment de la sortie de PHPStan 2.0
Faites auditer votre code PHP
Un regard extérieur sur votre base de code peut révéler des problèmes structurels que l'habitude fait oublier. Profitez d'un audit technique gratuit de 30 minutes.
Demander un audit gratuitArticles connexes

PHPStan niveau max sur un projet Symfony : les 10 erreurs que tu vas trouver
Passer PHPStan au niveau 10 sur un projet Symfony révèle des dizaines d'erreurs. Les 10 plus fréquentes et comment les corriger proprement.
Lire la suite →
Code mort en PHP : détecter et supprimer le code inutilisé
Identifier et éliminer le code inutilisé dans vos projets PHP pour améliorer la qualité, la maintenabilité et les performances.
Lire la suite →
Comment PHPStan peut vous aider à améliorer la qualité de votre code PHP
PHPStan est un outil de vérification statique pour PHP. Il examine votre code à la recherche d'erreurs potentielles et améliore la qualité du code.
Lire la suite →