jobsphp

Audit technique PHP : ce que la prod nous a appris

Gestion & Stratégie. Audit technique PHP : ce que la prod nous a appris

return $order->customer->account->billingAddress->country->isoCode…

Audit technique PHP: ce que la prod nous a appris

return $order->customer->account->billingAddress->country->isoCode;

Cette ligne peut passer les tests, fonctionner en production et rester dangereuse. Typage incomplet, propriété nullable, contrat implicite, chaîne d’accès non documentée: le compilateur PHP ne bloque rien si l’application ne lui fournit pas assez d’informations. La dette technique se cache rarement dans les erreurs visibles. Elle se loge dans les hypothèses non vérifiées.

Un audit technique PHP utile ne consiste donc pas à parcourir quelques fichiers en quête de mauvaises pratiques. Il mesure la qualité structurelle du code, la capacité de l’application à absorber la charge, la surface de sécurité, la couverture réelle des tests et le coût de chaque évolution. Le reste relève du commentaire esthétique.

Sur du code legacy, la difficulté n’est pas seulement de détecter les défauts. PHPStan, Psalm et les règles de qualité de SonarQube peuvent repérer des incohérences de typage, des contrats fragiles ou des constructions à risque. Xdebug intervient sur un autre terrain: il sert principalement au profilage de l’exécution et à la mesure de la couverture de code. Il ne détecte pas directement les défauts comme un analyseur statique. La difficulté consiste ensuite à convertir ces signaux différents en décisions de pilotage, sans interrompre la production, sans noyer l’équipe sous plusieurs milliers d’anomalies et sans transformer l’audit en inventaire stérile.

L’analyse statique comme premier rempart contre la dette technique

L’analyse statique intervient avant l’exécution. Elle examine le code, ses types, ses appels, ses retours et ses chemins logiques sans attendre qu’une requête déclenche le défaut en production.

Dans une application PHP ancienne, ce décalage est déterminant. Une erreur détectée dans un pipeline d’intégration continue coûte une correction localisée. La même erreur découverte après déploiement implique souvent une investigation, une reproduction, une correction urgente, une nouvelle livraison et parfois une restauration de données. Le coût n’est pas seulement technique. Il touche le planning, le support et la fiabilité du produit.

PHPStan et Psalm ciblent notamment:

  • les incohérences de typage entre paramètres et valeurs de retour;
  • les appels sur des valeurs potentiellement nulles;
  • les classes ou méthodes inexistantes;
  • les propriétés non initialisées;
  • les branches mortes ou inaccessibles;
  • certaines erreurs de manipulation de tableaux;
  • les contrats incompatibles entre interfaces, héritage et implémentations;
  • les flux de données dont le type ou la provenance devient incertain;
  • certaines constructions susceptibles de masquer des défauts de sécurité.

SonarQube complète cette approche par une plateforme de suivi de la qualité. Il peut signaler des bugs potentiels, des duplications, des problèmes de maintenabilité ou des vulnérabilités selon les règles activées. Il permet aussi de regrouper des résultats issus d’outils spécialisés et de suivre leur évolution dans le temps.

Xdebug, lui, observe l’application pendant qu’elle s’exécute. Son profilage aide à localiser les fonctions coûteuses, les appels répétés et les consommations de mémoire. Ses fonctions de couverture indiquent quels fichiers, lignes ou chemins ont effectivement été parcourus par un scénario de test. Ce sont des informations indispensables pour comprendre le comportement réel du système, mais elles ne remplacent ni PHPStan, ni Psalm, ni les règles de qualité de SonarQube.

Le bénéfice principal de l’analyse statique n’est pas le nombre d’erreurs remontées. C’est la réduction de l’incertitude. Un code strictement typé expose davantage son contrat. Un service qui annonce Customer mais retourne parfois null impose une décision explicite. Soit le contrat est faux, soit le comportement est faux. Dans les deux cas, l’analyse statique rend le problème visible.

PHPStan, Psalm, SonarQube et Xdebug: des périmètres différents

Ces outils ne produisent pas le même signal. Les opposer sur un classement unique n’a pas beaucoup de sens.

OutilFonction dominanteUsage pertinent dans un audit PHPLimite principale
PHPStanAnalyse statique et vérification des typesÉlever progressivement le niveau de fiabilité du codeNe mesure pas seul les performances réelles
PsalmAnalyse statique avancée et contrats de typesExaminer les flux de données et les incohérences complexesDemande une configuration cohérente avec le code existant
RectorRefactoring automatisé à partir de l’ASTModerniser le code et préparer une montée de version PHPLes transformations exigent des tests et une revue
XdebugProfilage et couverture de codeObserver l’exécution réelle et les chemins parcourusSon instrumentation peut modifier les performances
SonarQubeCentralisation de métriques et d’anomaliesSuivre la qualité, la sécurité et la dette dans le tempsLa plateforme ne remplace pas l’analyse des causes

PHPStan et Psalm travaillent principalement sur la structure du code et les contrats que celui-ci expose. Ils peuvent détecter une valeur potentiellement nulle ou une incompatibilité de type sans que le scénario concerné ait été exécuté. SonarQube ajoute une lecture transversale de la qualité, avec des règles et des indicateurs destinés à faciliter le suivi collectif.

Xdebug répond à une question différente: que se passe-t-il réellement lorsque le code tourne? Une fonction peut être parfaitement valide du point de vue du typage et monopoliser une part importante du temps d’exécution. À l’inverse, une classe signalée comme complexe peut être rarement appelée et ne pas constituer une priorité de performance.

SonarQube peut intégrer les rapports de PHPStan et de Psalm, directement ou via le format Generic Issue Import. Cette centralisation est utile pour le pilotage. Elle évite de disperser les alertes entre les sorties du pipeline, les tableaux de suivi et les tickets manuels.

Elle ne doit pas être confondue avec une analyse supplémentaire miraculeuse. Si les règles sont mal calibrées, SonarQube centralise surtout le bruit.

Un audit technique PHP ne mesure pas la quantité de code. Il mesure la quantité d’incertitude qu’une équipe doit absorber pour modifier ce code.

Stratégies de déploiement progressif avec PHPStan et les baselines

Lancer PHPStan au niveau maximal sur une base legacy est une décision facile à prendre et difficile à assumer. Le premier rapport peut mélanger des erreurs réelles, des types absents, des annotations obsolètes, des dépendances mal décrites et des choix historiques qui n’ont jamais été formalisés.

PHPStan propose dix niveaux de sévérité. Cette granularité permet de définir une trajectoire plutôt qu’un basculement brutal. Le niveau de départ dépend de la structure du projet, de la qualité des annotations, de la présence de tests et du degré de couplage entre les modules.

La progression efficace suit généralement une logique simple:

1. Mesurer l’état initial.

Le premier passage sert à établir une photographie. Il ne sert pas à ouvrir immédiatement des centaines de tickets. Les erreurs doivent être regroupées par nature: typage, nullabilité, interfaces, tableaux, dépendances ou configuration.

2. Créer une baseline.

La baseline neutralise les anomalies existantes dans le périmètre contrôlé. Le pipeline peut alors échouer sur les nouvelles régressions sans exiger la résolution instantanée de tout le passif.

3. Délimiter le périmètre protégé.

Il est souvent plus réaliste de commencer par un module, un dossier ou une fonctionnalité régulièrement modifiée. Une baseline trop large et opaque donne l’illusion que tout le projet est sous contrôle alors qu’elle masque des zones qui ne sont jamais analysées avec la même exigence.

4. Traiter les zones à risque.

Les modules qui manipulent des paiements, des droits d’accès, des données personnelles ou des traitements asynchrones passent avant les classes purement administratives. La criticité métier doit piloter la séquence.

5. Réduire la baseline.

Chaque correction retire une partie du bruit historique. La dette devient un stock visible et décroissant, pas une fatalité abstraite.

6. Élever le niveau de règles.

Une fois les erreurs les plus grossières résolues, l’équipe peut augmenter progressivement le niveau d’analyse. Chaque palier doit rester compatible avec la capacité de revue et de correction disponible.

La baseline n’est pas une dérogation permanente. C’est un mécanisme de migration. Elle devient contre-productive lorsqu’elle sert à masquer les nouvelles erreurs ou lorsqu’elle n’est jamais réévaluée. Une anomalie nouvelle ne doit pas pouvoir entrer dans la baseline simplement parce qu’elle gêne la livraison.

Le même principe vaut pour le déploiement. Une règle de qualité ne doit pas devenir un couperet incompréhensible qui bloque tous les changements, mais elle ne doit pas non plus rester informative au point de n’avoir aucune conséquence. Le compromis solide consiste à protéger les fichiers modifiés, les nouveaux modules ou les zones explicitement assainies, puis à élargir progressivement la contrainte.

Ce que le pilotage doit mesurer

Un comité technique n’a pas besoin d’un export de milliers de lignes. Il lui faut des indicateurs exploitables:

  • nombre d’anomalies nouvelles par branche ou par livraison;
  • évolution de la baseline;
  • répartition des défauts par sévérité;
  • modules les plus exposés;
  • temps moyen entre la détection et la correction;
  • part du code analysée;
  • taux d’échec du pipeline pour une nouvelle anomalie;
  • dépendances obsolètes ou incompatibles avec la version cible de PHP;
  • proportion des changements qui touchent des zones non couvertes par les tests.

Ces métriques ne donnent pas un retour sur investissement universel. Aucun chiffre sérieux ne permet de promettre une économie fixe pour chaque projet. Elles donnent mieux: une capacité à comparer l’état du système avant et après les décisions prises.

Dans le cadre d’un pilotage de projet web, cette visibilité change la discussion. La dette n’est plus une ligne vague dans un compte rendu. Elle devient une contrainte technique localisée, avec un coût de traitement et un risque associé.

Il faut également regarder la qualité du signal. Une baisse du nombre d’anomalies peut provenir d’une correction réelle, mais aussi d’une règle désactivée, d’un dossier exclu ou d’un changement de configuration. Les indicateurs doivent donc être lus avec leur périmètre d’analyse et leur historique de configuration. Sinon, le tableau de bord récompense la disparition des alertes plutôt que l’amélioration du code.

Automatiser la modernisation du code legacy avec Rector

Rector travaille sur l’AST, l’arbre syntaxique abstrait du code. Son intérêt apparaît lorsqu’une application doit évoluer vers une version plus récente de PHP ou appliquer de manière homogène une série de transformations.

Sur une base ancienne, la modernisation manuelle est lente et peu homogène. Un développeur corrige un motif dans un module. Un autre applique une variante ailleurs. La différence n’est pas toujours fonctionnelle, mais elle augmente le coût de maintenance. Rector permet d’automatiser certaines transformations répétitives:

  • adaptation de syntaxes devenues obsolètes;
  • remplacement de constructions compatibles avec une version plus récente;
  • modernisation progressive de signatures;
  • simplification de motifs répétitifs;
  • refactoring systématique fondé sur des règles;
  • préparation d’une montée de version de PHP.

La fin de support officiel de PHP 7.4, à la fin de 2022, puis celle de PHP 8.0, à la fin de 2023, ont accentué ce besoin de trajectoire. PHP 8.1 a atteint la fin de son support officiel à la fin de 2025. Une application qui reste sur une version ancienne accumule deux risques distincts: l’exposition aux défauts non corrigés et la réduction du nombre de bibliothèques compatibles.

Rector peut accélérer la migration. Il ne valide pas le comportement métier.

Une transformation syntaxiquement correcte peut modifier une hypothèse implicite. Un appel qui acceptait auparavant une valeur ambiguë peut devenir plus strict. Une méthode peut recevoir un type différent. Une branche rarement exécutée peut dépendre d’un comportement ancien. Les tests automatisés doivent donc encadrer chaque lot de transformations, et les zones critiques exigent une revue manuelle.

Le processus robuste tient en quatre contrôles:

  • exécuter Rector sur un périmètre réduit;
  • analyser le diff produit, pas seulement le résultat final;
  • relancer PHPStan ou Psalm après transformation;
  • exécuter les tests unitaires, d’intégration et les scénarios métier disponibles.

Une mise à jour automatique sans repasse de tests n’est pas une stratégie de modernisation. C’est une modification non maîtrisée avec une apparence industrielle.

Refactoring, migration et dette: trois sujets différents

Ces termes sont souvent mélangés dans les comités de direction technique.

Le refactoring modifie la structure interne sans changer le comportement attendu. Il réduit le couplage, clarifie les responsabilités ou améliore les contrats.

La migration déplace l’application vers une version de PHP, un framework ou une bibliothèque différente. Elle peut nécessiter du refactoring, mais son objectif premier est la compatibilité.

La réduction de dette cible les choix qui ralentissent les évolutions: duplications, dépendances obsolètes, absence de tests, conventions divergentes, accès directs à des données ou logique métier dispersée.

Rector intervient surtout sur les deux premiers sujets. Il ne résout pas une architecture incohérente. Il ne crée pas les tests manquants. Il ne décide pas qu’un service doit devenir une frontière de domaine. Cet arbitrage relève de l’architecture et du pilotage.

La bonne unité de travail n’est pas nécessairement le fichier. C’est souvent le motif de transformation. Une règle Rector sur un seul type de syntaxe, appliquée à un périmètre court et vérifiable, produit un résultat plus lisible qu’un grand passage qui mélange migration, nettoyage et réorganisation. Plus le diff est large, plus il devient difficile de relier une régression à sa cause.

Cartographier les performances réelles, pas les suppositions

Une analyse statique ne mesure pas le temps d’exécution d’une requête. Elle peut détecter des constructions risquées ou révéler une complexité structurelle. Elle ne dira pas qu’une requête SQL lente monopolise un worker PHP, qu’un appel réseau est répété dans une boucle ou qu’une allocation mémoire provoque une dégradation sous charge.

Le profilage répond à une autre question: où le temps et la mémoire sont-ils réellement consommés?

Xdebug fournit des fonctions de couverture de code avec xdebug_start_code_coverage et xdebug_get_code_coverage. Elles permettent d’identifier la part exacte du code exécutée pendant des tests ou des requêtes. Cette donnée est utile pour distinguer la couverture déclarée de la couverture observée.

Un pourcentage global peut donner une impression de maîtrise tout en masquant des zones critiques jamais parcourues. Une classe de facturation couverte à 90 % par le nombre de lignes peut rester mal testée si les cas d’erreur, de concurrence ou de reprise ne sont jamais exécutés.

La couverture doit donc être lue avec plusieurs dimensions:

  • couverture des lignes;
  • couverture des branches;
  • couverture des chemins métier;
  • couverture des erreurs et exceptions;
  • couverture des interactions avec les dépendances externes;
  • couverture des traitements asynchrones;
  • couverture des cas limites sur les données.

Xdebug n’est pas un instrument neutre. Son instrumentation peut modifier les performances. Il ne faut pas utiliser un environnement profilé comme référence directe pour annoncer un temps de réponse en production. Le profilage sert à localiser et comparer les coûts, pas à remplacer une campagne de charge.

Cette distinction est importante dans un audit technique d’application web. Un profil Xdebug réalisé sur un poste de développement permet d’identifier une zone coûteuse ou une répétition inattendue. Il ne suffit pas à conclure sur le comportement de PHP-FPM, sur la saturation d’un pool de workers ou sur l’impact d’une dépendance externe. Pour ces questions, il faut confronter les observations du profilage aux métriques d’exécution et à des scénarios représentatifs.

Le découpage minimal d’un audit de performance

Une latence globale ne suffit pas à désigner la cause. L’analyse doit distinguer les couches:

1. Le temps d’entrée dans l’application.

Bootstrap du framework, chargement de la configuration, résolution des dépendances et initialisation des services.

2. Le temps d’exécution PHP.

Fonctions coûteuses, boucles, sérialisation, allocation mémoire, ramasse-miettes et appels répétés.

3. Les accès aux données.

Requêtes SQL, volume des résultats, indexation, N+1, transactions longues et verrous.

4. Les dépendances externes.

API tierces, services de paiement, stockage distant, files de messages et résolution DNS.

5. La couche d’exécution.

PHP-FPM, workers, limites mémoire, pool de processus, OPcache et stratégie de déploiement.

6. Le comportement sous concurrence.

Saturation des workers, contention, files d’attente et dégradation progressive de la latence.

Le résultat attendu n’est pas une liste de fonctions lentes. C’est une carte des contraintes. Une fonction peut être coûteuse mais rarement appelée. Une autre peut être modérément coûteuse et exécutée sur chaque requête. La priorité dépend du produit de la fréquence, du coût et de la criticité.

Il faut aussi distinguer le coût moyen du comportement dégradé. Une moyenne acceptable peut cacher des requêtes qui attendent une dépendance externe, des traitements qui consomment progressivement la mémoire ou des files qui se forment uniquement lors d’un pic. L’audit ne doit pas chercher un chiffre unique pour résumer la performance. Il doit identifier les conditions dans lesquelles l’application cesse de tenir son contrat.

Le profilage ne répond pas à la question « quelle fonction est lente? ». Il répond à la question « quelle contrainte limite le système dans ses conditions réelles d’exécution? ».

Centraliser les indicateurs de qualité dans SonarQube

SonarQube dispose de plus de 270 règles d’analyse pour PHP. Ce volume permet de couvrir plusieurs dimensions: bugs potentiels, vulnérabilités, duplications, dette de maintenance et conventions de qualité.

La quantité de règles ne constitue pas une stratégie. Activer tout le catalogue sur une application historique produit souvent un flux d’anomalies impossible à traiter. Les équipes finissent par ignorer les alertes, ce qui détruit la valeur du dispositif.

La configuration doit partir des risques du système:

  • règles de sécurité sur les entrées utilisateur et les accès;
  • règles de fiabilité sur les traitements financiers ou transactionnels;
  • règles de typage et de nullabilité sur les domaines critiques;
  • règles de complexité sur les services fréquemment modifiés;
  • règles de duplication sur les composants qui divergent rapidement;
  • règles de conception sur les dépendances entre modules.

L’intégration des rapports PHPStan et Psalm permet de rapprocher l’analyse spécialisée du suivi global. Les outils conservent leur rôle technique. SonarQube fournit une vue commune pour les équipes de développement, les responsables de produit et la direction technique.

Cette vue doit rester orientée vers les décisions. Un tableau de bord pertinent permet de répondre rapidement à cinq questions:

  • Les nouvelles modifications dégradent-elles la qualité?
  • Quels modules concentrent le risque?
  • La dette diminue-t-elle réellement?
  • Les défauts critiques sont-ils corrigés dans les délais définis?
  • L’application se rapproche-t-elle de la version cible de PHP?

Le principe de la quality gate est utile lorsqu’il s’applique aux changements nouveaux. Bloquer immédiatement toute livraison à cause de défauts historiques revient à déclarer le projet gelé. Laisser passer toutes les nouvelles anomalies revient à maintenir le statu quo. La règle opérationnelle doit donc protéger le périmètre assaini sans prétendre réparer tout le passé en une seule itération.

Le format et les conventions comptent aussi

L’export des résultats doit rester exploitable par les outils et compréhensible par les personnes qui prennent les décisions. Une anomalie importée dans SonarQube sans fichier, ligne, règle ou niveau de gravité clairement identifiable devient rapidement un ticket sans propriétaire.

Les conventions de l’équipe ont la même importance que le format technique. Il faut savoir ce qui déclenche un échec de pipeline, ce qui crée un ticket, ce qui est traité dans le sprint et ce qui reste dans le stock de dette. Sans cette distinction, toutes les alertes semblent urgentes et aucune ne l’est vraiment.

Un indicateur de qualité n’a de valeur que s’il entraîne une action identifiable. La complexité d’un service peut justifier une revue d’architecture, une duplication persistante peut signaler une frontière de module mal définie, et une vulnérabilité critique peut imposer une correction avant la prochaine livraison. En revanche, une variation mineure d’un score global ne mérite pas automatiquement une réunion.

SonarQube ne doit pas devenir une vitrine destinée à prouver que l’équipe utilise un outil. Il doit rendre visibles les tendances et les arbitrages. La question n’est pas de savoir si le projet affiche une note satisfaisante, mais si l’équipe comprend pourquoi cette note évolue et ce qu’elle doit faire ensuite.

Ce que l’audit change dans la production

Un audit technique PHP n’a d’intérêt que s’il modifie les décisions prises autour du code. Un rapport qui reste dans un espace documentaire ne réduit ni la dette, ni le risque, ni le temps de correction.

La première décision consiste à séparer les signaux. L’analyse statique examine les défauts potentiels sans exécuter le programme. Le profilage mesure les coûts observés pendant l’exécution. La couverture indique les chemins parcourus par les tests. SonarQube rassemble une partie de ces informations pour faciliter le suivi, mais il ne remplace pas l’interprétation technique.

La deuxième décision consiste à choisir un rythme réaliste. Une base legacy ne devient pas saine parce qu’un outil a été activé. Elle progresse lorsque les nouvelles modifications sont plus sûres que les anciennes, lorsque la baseline diminue et lorsque les modules critiques sont traités avec une priorité explicite.

La troisième consiste à protéger la production sans utiliser la production comme seul environnement de vérification. Les tests, l’analyse statique, les revues de diff, le profilage ciblé et les déploiements progressifs réduisent chacun une catégorie d’incertitude. Aucun ne couvre à lui seul l’ensemble du risque.

C’est finalement ce que la prod nous a appris: la qualité technique n’est pas une propriété déclarée une fois pour toutes. Elle se construit dans la façon de détecter un problème, de le prioriser, de le corriger et de vérifier que la correction n’a pas déplacé le risque ailleurs.

Un audit technique PHP réussi ne promet pas un code parfait. Il rend les compromis visibles, les régressions plus difficiles et les évolutions moins dépendantes de suppositions silencieuses. C’est déjà beaucoup plus utile qu’un inventaire de mauvaises pratiques.

Questions fréquentes

À quoi sert un audit technique PHP ?
Il mesure la qualité structurelle du code, la capacité de l’application à absorber la charge, sa surface de sécurité, la couverture réelle des tests et le coût des évolutions. Son objectif est de rendre visibles les incertitudes et d’aider à prendre des décisions de pilotage.
Quelle est la différence entre PHPStan, Psalm, SonarQube et Xdebug ?
PHPStan et Psalm analysent principalement la structure du code, les types et les contrats sans exécuter l’application. SonarQube centralise des métriques et des anomalies, tandis que Xdebug observe l’exécution, notamment le profilage et la couverture du code.
Comment utiliser une baseline avec PHPStan sur une application ancienne ?
La baseline neutralise les anomalies existantes dans le périmètre contrôlé afin que le pipeline puisse bloquer les nouvelles régressions. Elle doit ensuite être réduite progressivement, en commençant par les modules à risque et les zones régulièrement modifiées.
Rector peut-il moderniser automatiquement une application PHP sans risque ?
Rector automatise certaines transformations syntaxiques et certains refactorings, mais il ne valide pas le comportement métier. Il faut limiter chaque lot, examiner le diff, relancer PHPStan ou Psalm et exécuter les tests disponibles.
Xdebug permet-il de mesurer directement les performances de la production ?
Non. Son instrumentation peut modifier les performances, et un profil réalisé en développement ne suffit pas pour conclure sur PHP-FPM, la saturation des workers ou l’impact des dépendances externes. Le profilage sert à localiser et comparer les coûts, en complément de métriques d’exécution et de scénarios représentatifs.