Comment PHPStan peut vous aider à améliorer la qualité de votre code PHP
Par Louis-Arnaud Catoire
Mis à jour le

L'analyse statique : un compilateur pour PHP
PHP est un langage interprété. Contrairement à Java ou Go, aucune phase de compilation ne vérifie la cohérence des types avant l'exécution. Pendant des années, les développeurs PHP ont découvert leurs erreurs de typage en production, à travers des TypeError ou des null inattendus. L'analyse statique comble ce vide : elle simule une compilation en parcourant l'AST (Abstract Syntax Tree) du code source, sans jamais l'exécuter.
PHPStan est l'outil de référence dans cet espace. Il parse votre code, reconstruit un graphe de types et applique des règles de vérification sur ce graphe. Là où les tests unitaires vérifient des comportements, PHPStan vérifie des invariants structurels : cohérence des signatures, nullabilité, exhaustivité des unions. Les deux approches sont complémentaires et non substituables.
Installation et première exécution
L'installation se fait via Composer, le gestionnaire de dépendances PHP :
composer require --dev phpstan/phpstan
Puis lancez l'analyse sur votre code :
./vendor/bin/phpstan analyse src
Centralisez la configuration dans un fichier phpstan.neon à la racine du projet :
parameters:
level: 6
paths:
- src
excludePaths:
- src/Migrations
Avec ce fichier en place, un simple ./vendor/bin/phpstan analyse suffit.
Les niveaux de règles : une adoption progressive
PHPStan propose 11 niveaux de règles, de 0 à 10. Chaque niveau inclut les vérifications du précédent.
Niveaux 0-2 couvrent les bases : classes et fonctions inconnues, nombre d'arguments incorrect, variables potentiellement non définies, types inconnus sur les expressions mixed.
Niveaux 3-5 ajoutent la vérification systématique des types de retour, la détection des appels sur des types nullables, et le contrôle des arguments passés aux fonctions.
Niveau 6 est le seuil recommandé pour les projets matures. Le typage strict entre en jeu : les annotations manquantes sont signalées. En dessous, PHPStan tolère l'implicite. Au-dessus, il l'interdit.
Niveaux 7-9 affinent les unions, les intersections, la nullabilité imbriquée. Le niveau 9 traite mixed comme un type opaque qui doit être narrowé avant utilisation, ce qui force une rigueur comparable à un langage fortement typé.
Pour un projet existant, commencez au niveau 0 et montez progressivement. Pour un nouveau projet, visez directement le niveau 6.
Exemples concrets de bugs détectés
Un appel de méthode sur un type potentiellement null :
function getUsername(User $user): string
{
return $user->getProfile()->getName();
}
Si getProfile() peut retourner null, PHPStan signale : Cannot call method getName() on null. Ce type d'erreur passe systématiquement en code review humaine parce que le chemin nominal fonctionne.
Un type de retour incohérent :
function findUser(int $id): User
{
$user = $this->repository->find($id);
return $user;
}
Si find() retourne User|null, PHPStan détecte l'incohérence. En production, c'est un crash sur le premier appel avec un ID invalide.
PHPStan excelle aussi à repérer le code mort dans vos projets, ces branches jamais atteintes qui alourdissent la base de code. Coupler PHPStan avec des tests automatisés garantit une couverture maximale des erreurs, tant structurelles que comportementales.
Une condition dupliquée, signe de copier-coller :
if ($status === 'active' || $status === 'active') {
PHPStan repère la redondance. La deuxième condition masque probablement un autre état qui n'est jamais vérifié.
Besoin d'accompagnement sur votre projet ?
Parlons-enLa baseline : dette technique maîtrisée
Sur un projet existant, lancer PHPStan au niveau 6 peut générer des centaines d'erreurs. Cette accumulation est un symptôme classique de dette technique qu'il faut traiter méthodiquement. La baseline résout ce problème en gelant l'état actuel :
./vendor/bin/phpstan analyse --generate-baseline
Cette commande crée un fichier phpstan-baseline.neon qui liste toutes les erreurs existantes. Référencez-le dans votre configuration :
includes:
- phpstan-baseline.neon
parameters:
level: 6
paths:
- src
À partir de ce moment, seules les nouvelles erreurs sont signalées. La baseline n'est pas un passe-droit permanent : c'est un outil de gestion de la dette technique. Traitez-la comme un backlog. Chaque sprint, corrigez un lot d'erreurs et regénérez la baseline. Suivez son évolution : une baseline qui grossit est un signal d'alerte pour un lead.
Un réflexe utile : intégrez dans votre CI un check qui compte les entrées de la baseline et échoue si ce nombre augmente. Cela empêche l'accumulation silencieuse de dette.
Intégration CI et workflow d'équipe
PHPStan prend toute sa valeur dans un pipeline d'intégration continue. Il s'intègre naturellement aux côtés de PHP-CS-Fixer et des conventions de codage PHP pour former un pipeline de qualité complet. Dans un .gitlab-ci.yml :
phpstan:
stage: test
script:
- composer install
- vendor/bin/phpstan analyse --no-progress
Chaque merge request est vérifiée automatiquement. Si PHPStan échoue, le développeur corrige avant de merger. Le --no-progress supprime l'affichage de la barre de progression, inutile dans les logs CI.
Pour les équipes avec un cache CI, ajoutez --error-format=json et exploitez la sortie pour des dashboards de qualité ou des notifications Slack ciblées.
L'extension phpstan-symfony : aller au-delà du code vanilla
Les frameworks PHP utilisent massivement des patterns que l'analyse statique ne peut pas résoudre seule : injection de dépendances, configuration YAML, méthodes magiques. L'extension phpstan-symfony comble ce fossé :
composer require --dev phpstan/phpstan-symfony
includes:
- vendor/phpstan/phpstan-symfony/extension.neon
parameters:
level: 6
paths:
- src
symfony:
containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml
Avec cette configuration, PHPStan comprend les types retournés par $container->get(), valide les noms de services, type correctement les objets Request et InputInterface. Sans cette extension, des dizaines de faux positifs polluent l'analyse et poussent les développeurs à ignorer les vrais problèmes.
Règles custom : PHPStan comme gardien d'architecture
C'est ici que PHPStan cesse d'être un simple linter et devient un outil d'architecture. Vous pouvez écrire des règles custom qui encodent les décisions structurelles de votre projet.
Prenons un exemple concret : interdire qu'un service du domaine dépende directement de l'infrastructure.
use PHPStan\Rules\Rule;
use PHPStan\Analyser\Scope;
use PhpParser\Node;
use PhpParser\Node\Stmt\Use_;
/**
* @implements Rule<Use_>
*/
class NoDomainToInfrastructureDependencyRule implements Rule
{
public function getNodeType(): string
{
return Use_::class;
}
public function processNode(Node $node, Scope $scope): array
{
$file = $scope->getFile();
if (!str_contains($file, '/Domain/')) {
return [];
}
foreach ($node->uses as $use) {
$name = $use->name->toString();
if (str_contains($name, 'Infrastructure\\')) {
return [
'Les classes du Domain ne doivent pas importer depuis Infrastructure.',
];
}
}
return [];
}
}
Cette règle transforme une convention verbale ("le domaine ne dépend pas de l'infra") en une contrainte vérifiée à chaque commit. C'est la différence entre un ADR (Architecture Decision Record) que personne ne lit et une garde-fou automatisée.
Enregistrez vos règles custom dans la configuration :
services:
-
class: App\PHPStan\NoDomainToInfrastructureDependencyRule
tags:
- phpstan.rules.rule
D'autres cas d'usage courants pour les règles custom : interdire l'utilisation de new dans les controllers (tout doit passer par l'injection), forcer l'usage de value objects pour certains paramètres, ou empêcher les dépendances circulaires entre bounded contexts. Ce type de règle rejoint la philosophie du domaine qui ne devrait jamais connaître Symfony, où PHPStan devient le gardien des frontières architecturales.
Le système de types avancé : generics et template types
À partir du niveau 7, PHPStan exploite pleinement son système de types. Les generics PHP via les annotations @template permettent d'exprimer des contrats que le langage natif ne supporte pas.
/**
* @template T of object
* @param class-string<T> $className
* @return T
*/
function create(string $className): object
{
return new $className();
}
Avec cette annotation, PHPStan sait que create(User::class) retourne un User, pas un object générique. Ce niveau de précision se propage dans tout le graphe de types et élimine des catégories entières de bugs liés au downcasting.
Les template types brillent dans les repositories, les collections typées et les factories. Un Repository<User> dont la méthode find() retourne User|null au lieu de object|null : c'est ce niveau de typage qui rapproche PHP d'un langage à typage fort sans sacrifier sa flexibilité.
Les conditional return types (@return ($flag is true ? Foo : Bar)) et les assertions (@phpstan-assert) complètent l'outillage pour modéliser des API complexes que le type system natif de PHP ne peut pas capturer.
PHPStan comme philosophie de design
Au-delà de l'outil, adopter PHPStan au niveau max transforme la façon de concevoir du code. L'obligation de satisfaire l'analyseur pousse vers des designs plus explicites : moins de mixed, moins de magie, des contrats clairs entre les couches. Le code devient sa propre documentation. Pour découvrir les erreurs concrètes les plus fréquentes à ce niveau, l'article sur les 10 erreurs PHPStan niveau max sur un projet Symfony fournit des exemples et des corrections directement applicables.
Un architecte qui configure PHPStan avec des règles custom, des generics et un niveau 9 ne cherche pas à satisfaire un outil. Il encode les invariants de son système dans un vérificateur automatique qui tourne à chaque commit. C'est de la gouvernance technique exécutable. Notre service d'audit de code PHP s'appuie sur ce type d'outillage pour identifier les failles structurelles d'un projet, première étape d'un parcours de modernisation applicative.
Pour aller plus loin
- PHPStan 2.0 : niveau 10 et nouvelles fonctionnalités, les nouveautés de la version 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 gratuitQuestions fréquentes
PHPStan est un outil d'analyse statique qui détecte les erreurs dans votre code PHP sans l'exécuter. Il vérifie la cohérence des types, les appels de méthodes inexistantes, les variables non définies et les problèmes de logique. C'est un complément aux tests unitaires qui attrape les bugs avant qu'ils n'arrivent en production.
Commencez par le niveau 1 et montez progressivement. Le niveau 5 est un bon objectif pour la plupart des projets : il vérifie les types de retour, les paramètres et les propriétés. Les niveaux 8 à 10 imposent un typage strict quasi complet, idéal pour les projets critiques mais plus exigeant à maintenir.
Oui, c'est même la pratique recommandée. PHPStan s'exécute en ligne de commande et retourne un code d'erreur non nul si des problèmes sont détectés. Il s'intègre dans GitLab CI, GitHub Actions ou tout autre outil de CI en une seule ligne de configuration.
Articles 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 →
PHPStan 2.0 : niveau 10 et nouvelles fonctionnalités
PHPStan 2.0 introduit le niveau 10 pour une analyse statique PHP encore plus stricte. Découvrez les nouvelles fonctionnalités pour un code impeccable.
Lire la suite →