Xdebug : installer et activer Xdebug 3 pour PHP CLI et web

Guide complet pour installer et activer Xdebug 3 sur PHP 8.2/8.3 — config CLI, PHP‑FPM, Docker, déclenchement DBGp, et recommandations perf/sécurité.

Ordinateur avec écrans affichant du code et graphiques de débogage.

Table des matières :

  1. Installer Xdebug 3 proprement (PHP 8.2/8.3) sans casser CLI vs FPM
  2. Activer Xdebug pour PHP CLI (Composer, scripts, PHPUnit) sans le subir au quotidien
  3. Activer Xdebug côté web : PHP-FPM, Apache, Nginx (et cas FrankenPHP)
  4. Vérifier que Xdebug est chargé et que vous modifiez le bon php.ini
  5. Déclencher une session de debug (DBGp), comprendre client_host, et éviter les faux négatifs
  6. Garde-fous en contexte PrestaShop : performance, OPcache, sécurité, et alternatives au “debug permanent”

Installer Xdebug 3 proprement (PHP 8.2/8.3) sans casser CLI vs FPM

Sur un serveur PrestaShop, le piège classique n’est pas « installer Xdebug », c’est installer Xdebug pour le mauvais SAPI. PHP CLI, PHP-FPM et (plus rarement en 2026) Apache mod_php n’ont pas forcément le même php.ini ni le même dossier conf.d. Résultat : vous avez Xdebug en ligne de commande mais pas sur le web (ou l’inverse), et vous perdez 30 minutes à « débugger le debugger ». Les exemples ci-dessous partent sur Ubuntu/Debian avec PHP 8.2 ou 8.3 (courants en PrestaShop 8.x et dans les environnements de préprod de PrestaShop 9).

Avant d’installer quoi que ce soit, prenez 20 secondes pour confirmer les versions réellement utilisées :

php -v
php-fpm8.2 -v 2>/dev/null || true
php-fpm8.3 -v 2>/dev/null || true

Sur Debian/Ubuntu, la séparation des fichiers de conf aide beaucoup. En pratique, retenez ce mini-mémo (les chemins peuvent varier légèrement selon la distribution, mais c’est le cas le plus fréquent) :

Contexte Binaire / service Dossier de conf principal Dossier des .ini scannés
CLI php /etc/php/8.x/cli/php.ini /etc/php/8.x/cli/conf.d/
FPM php8.x-fpm /etc/php/8.x/fpm/php.ini /etc/php/8.x/fpm/conf.d/

Le plus stable, en environnement proche de la production, reste le paquet de la distribution, parce qu’il gère les dépendances et la bonne ABI de PHP. Exemple Ubuntu avec Ondřej Surý (ou dépôts officiels si disponibles) :

sudo apt update
sudo apt install php8.2-xdebug
# ou
sudo apt install php8.3-xdebug

Ensuite, vérifiez où l’extension est activée. Sur Debian/Ubuntu, la séparation est explicite : /etc/php/8.2/cli/conf.d/ vs /etc/php/8.2/fpm/conf.d/. La commande phpenmod/phpdismod peut activer/désactiver par version, mais elle ne « répare » pas une configuration incohérente.

Exemples utiles en multi-versions :

# Voir ce que le paquet a mis à disposition
ls -l /etc/php/8.2/mods-available | grep -i xdebug || true

# Activer uniquement pour le SAPI voulu (ici: FPM 8.2)
sudo phpenmod -v 8.2 -s fpm xdebug

# Désactiver côté CLI si vous ne voulez pas le subir en continu
sudo phpdismod -v 8.2 -s cli xdebug

En cas de multi-versions, vérifiez aussi l’alternative active (update-alternatives --config php) pour éviter d’installer Xdebug 8.3 et d’exécuter du CLI en 8.2.

Si vous êtes en image Docker php:* ou sur une distro sans paquet, PECL reste le plan B standard :

pecl install xdebug
# puis dans un .ini (voir sections suivantes)
# zend_extension=xdebug

Attention : PECL compile contre les headers de la version exacte de PHP du conteneur/serveur. Sur un serveur qui a plusieurs binaires PHP, compiler dans le mauvais contexte produit souvent un .so inutilisable au runtime (ou des segfaults). Deux garde-fous simples :

  • compilez dans le même environnement que celui qui exécute PHP (même conteneur, même image, même version) ;
  • vérifiez après installation que le module se charge bien (voir section « Vérifier… »).

Documentation officielle : installation et matrices de versions sur xdebug.org/docs/install.
Détail important : Xdebug est une Zend extension, donc l’instruction est zend_extension=… (pas extension=…).

Activer Xdebug pour PHP CLI (Composer, scripts, PHPUnit) sans le subir au quotidien

En CLI, activer Xdebug « en dur » (xdebug.mode=debug + start_with_request=yes) est rarement un bon choix : Composer et les scripts de build (y compris ceux liés aux modules PrestaShop) vont ralentir, et certaines tâches deviennent pénibles (timeouts CI, consommation mémoire, etc.). L’approche robuste consiste à désactiver par défaut et à n’activer le debug que sur demande, soit par variable d’environnement, soit via php -d ponctuel.

Configuration CLI typique dans un fichier dédié (ex. /etc/php/8.2/cli/conf.d/99-xdebug.ini) :

zend_extension=xdebug
xdebug.mode=off
xdebug.start_with_request=trigger
xdebug.client_port=9003
xdebug.log_level=0

Pourquoi mode=off + start_with_request=trigger ?

  • mode=off garantit qu’un php classique (Composer, scripts cron, tâches CI non concernées) n’embarque pas le coût de Xdebug.
  • trigger évite les faux positifs (debug lancé partout) tout en restant très simple à activer quand vous en avez besoin.

Avec ça, vous activez au cas par cas :

# Activer juste pour UNE commande
XDEBUG_MODE=debug XDEBUG_TRIGGER=1 php bin/console list

# Ou via -d, pratique en CI
php -d xdebug.mode=debug -d xdebug.start_with_request=yes your_script.php

Mini-scenario typique côté PrestaShop : vous exécutez un script CLI d’import catalogue (ou une commande Symfony en PrestaShop 8/9). Sans trigger, l’import garde ses perfs habituelles. Dès qu’un bug « à la 3e itération » apparaît, vous relancez la même commande avec XDEBUG_MODE=debug XDEBUG_TRIGGER=1 et vous placez un breakpoint au bon endroit.

Pour la couverture de code, Xdebug reste très utilisé mais coûteux. Xdebug lui-même rappelle dans sa documentation que le mode “coverage” ajoute une surcharge non négligeable (collecte des informations d’exécution). L’idée est de l’activer uniquement sur les jobs de tests concernés :

XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text

Si votre objectif est uniquement la couverture, vous pouvez aussi évaluer PCOV, mais en contexte PrestaShop/monolithe + modules, Xdebug est souvent déjà l’outil le plus simple à standardiser (un seul outil pour debug + traces + coverage).

Astuce pratique pour éviter les confusions : vérifiez que le CLI lit bien votre .ini au moment où vous testez, surtout si vous utilisez plusieurs versions :

php --ini
php -i | grep -i "xdebug.mode" || true

Activer Xdebug côté web : PHP-FPM, Apache, Nginx (et cas FrankenPHP)

Côté web, la règle est : la config doit vivre dans le SAPI réellement utilisé par le serveur HTTP. Sur la majorité des stacks actuelles (Nginx + PHP-FPM, ou Apache + proxy_fcgi), cela veut dire /etc/php/8.2/fpm/conf.d/. Ne copiez pas aveuglément la config CLI. Le web a des contraintes différentes : reverse proxy, conteneurs, NAT, et surtout risque de sécurité si vous exposez un débogage distant.

Exemple minimal de configuration FPM (ex. /etc/php/8.2/fpm/conf.d/99-xdebug.ini) :

zend_extension=xdebug
xdebug.mode=debug,develop
xdebug.start_with_request=trigger
xdebug.client_port=9003

; Si votre navigateur/IDE est sur la même machine :
xdebug.client_host=127.0.0.1

; Si vous êtes derrière reverse proxy / Docker Desktop / WSL2, testez :
;xdebug.discover_client_host=1

; Log utile en dépannage (désactiver ensuite)
;xdebug.log=/var/log/php/xdebug.log
;xdebug.log_level=7

Pourquoi debug,develop côté web ?

  • debug pour le pas-à-pas,
  • develop pour des erreurs plus lisibles (stack traces améliorées, var_dump plus riche), utile en préprod/dev.

Après modification, redémarrage obligatoire :

sudo systemctl restart php8.2-fpm
sudo systemctl reload nginx   # ou restart apache2 si vous êtes sous Apache

Points d’attention très concrets en FPM :

  • Les fichiers .ini sont parfois chargés dans un ordre inattendu : un 20-xdebug.ini généré par paquet peut être surchargé par votre 99-xdebug.ini (ou l’inverse si vous mettez un numéro trop bas).
  • Si vous utilisez plusieurs pools FPM (ex : www pour la boutique, admin pour back-office sur un vhost différent), vous pouvez isoler Xdebug sur un pool dédié et laisser le pool principal sans Xdebug — c’est souvent le meilleur compromis « prod-like ».

Pour Docker, un cas fréquent est : votre IDE tourne sur l’hôte, PHP tourne dans un conteneur. Dans ce cas :

  • 127.0.0.1 dans le conteneur ne pointe pas vers votre machine,
  • vous devrez viser une IP atteignable (gateway, IP LAN) ou un alias comme host.docker.internal (disponible selon plateformes/paramétrage).

Pour FrankenPHP, le principe reste identique : Xdebug est une extension Zend, donc chargeable via zend_extension. Mais FrankenPHP étant conçu pour pousser les performances (HTTP/3, TLS auto, etc.), garder Xdebug en permanence contredit l’objectif. Si vous testez FrankenPHP sur une préprod technique, activez Xdebug uniquement sur un environnement dédié (ou au moins via trigger). Voir aussi l’article interne : découvrir FrankenPHP avec Caddy et HTTP/3.

Vérifier que Xdebug est chargé et que vous modifiez le bon php.ini

Ne supposez rien : validez. Pour le CLI, c’est trivial :

php -v
php --ri xdebug
php --ini

php --ini est particulièrement utile pour voir le fichier principal et la liste des .ini scannés (et donc repérer un doublon).

Côté FPM, vous pouvez obtenir une sortie équivalente avec :

php-fpm8.2 -i | grep -i -E 'xdebug|Scan this dir|Loaded Configuration'

Autre méthode (souvent la plus rapide quand vous n’êtes pas sûr du SAPI réellement sollicité par le vhost) : un phpinfo() temporaire protégé (IP allowlist, Basic Auth, ou au minimum suppression immédiate après vérification). Dans un contexte PrestaShop, évitez de laisser un fichier phpinfo.php accessible : c’est un inventaire de votre surface d’attaque (versions, modules, chemins, headers, etc.).

Checklist de vérification « ça ne marche pas » (très fréquente) :

  • le navigateur arrive-t-il bien sur le serveur FPM attendu (bon vhost / bon conteneur) ?
  • votre IDE écoute-t-il bien sur 9003 ?
  • Xdebug apparaît-il dans la section modules de phpinfo() du même SAPI ?
  • le log Xdebug (si activé) indique-t-il une tentative de connexion ?

Erreur fréquente : Cannot load Xdebug - it was already loaded. Elle arrive quand vous avez à la fois un zend_extension=xdebug dans php.ini et un fichier conf.d/20-xdebug.ini. Pour diagnostiquer proprement :

grep -R "xdebug" -n /etc/php/8.2/*/php.ini /etc/php/8.2/*/conf.d /etc/php/8.2/mods-available

Une autre erreur qui fait perdre du temps : Xdebug est bien chargé, mais aucun debug ne démarre parce que xdebug.start_with_request est à default (ou no) et que vous n’envoyez pas de trigger. Dans ce cas, activez le log temporairement (xdebug.log_level=7) et relisez les tentatives de connexion : vous verrez immédiatement si le problème est le host/port, le firewall, ou un IDE qui n’écoute pas.

Déclencher une session de debug (DBGp), comprendre client_host, et éviter les faux négatifs

Le debug pas-à-pas Xdebug utilise le protocole DBGp et, en Xdebug 3, le port par défaut est 9003 (et non plus 9000 comme dans beaucoup d’anciens tutos Xdebug 2). Référence : documentation officielle Step Debugging xdebug.org/docs/step_debug.

Avec start_with_request=trigger, vous contrôlez l’activation. En web, vous pouvez déclencher via querystring ou cookie :

  • URL : https://votre-site.test/?XDEBUG_TRIGGER=1
  • Cookie : XDEBUG_TRIGGER=1 (via extension navigateur ou manuel)

En CLI, c’est la même mécanique :

XDEBUG_MODE=debug XDEBUG_TRIGGER=1 php your_script.php

Le point non-négociable en environnement containerisé : le client_host vu par PHP (dans le conteneur) n’est pas forcément l’IP de votre machine hôte. Selon Docker/WSL2/VPN, 127.0.0.1 pointe… vers le conteneur lui-même. Deux stratégies :

1) Fixer explicitement xdebug.client_host vers une IP routable (gateway Docker, IP LAN).
2) Tenter xdebug.discover_client_host=1 en vous assurant que votre reverse proxy transmet bien l’IP source, et en comprenant les implications de confiance sur les en-têtes (évitez de l’activer « au hasard » sur des environnements exposés).

Cas très courant « dev sur laptop, serveur de préprod distant » : votre poste n’est pas directement joignable depuis le serveur (NAT, firewall entreprise, etc.). Une méthode simple et généralement acceptable en entreprise consiste à passer par un tunnel SSH reverse, sans ouvrir de port entrant sur votre machine :

# À lancer depuis votre machine (l’IDE écoute sur 9003 en local)
ssh -R 9003:127.0.0.1:9003 user@votre-serveur-preprod

Ensuite, côté serveur, vous pouvez mettre :

xdebug.client_host=127.0.0.1
xdebug.client_port=9003

Ainsi, Xdebug se connecte à 127.0.0.1:9003 sur le serveur, et SSH transfère ce flux vers votre IDE en local. C’est souvent plus simple (et plus sûr) que d’essayer de rendre votre poste directement accessible depuis un datacenter.

Pour la configuration IDE (VS Code), ne dupliquez pas ici : l’article interne couvre précisément les launch.json, path mappings et l’écoute sur 9003 : configurer Xdebug dans VS Code (mappings, écoute, ports).

Garde-fous en contexte PrestaShop : performance, OPcache, sécurité, et alternatives au “debug permanent”

Xdebug en mode debug a un coût, et en mode coverage/profiling il peut devenir violent. Sur une boutique PrestaShop avec un catalogue conséquent, vous allez le ressentir immédiatement : pages plus lentes, FPM workers occupés plus longtemps, et un bruit énorme si vous profilez à la volée. Si vous travaillez sur des sujets perf, gardez une séparation stricte : Xdebug pour le debug fonctionnel, et des outils de profiling dédiés (Blackfire, Tideways, etc.) pour le diagnostic de latence. Vous pouvez croiser avec des méthodes plus “runtime” via l’article : PrestaShop debug profiling : activer et analyser performances SQL.

Deux interactions à anticiper : OPcache/JIT et les caches applicatifs. Dans la pratique, dès que vous chargez Xdebug, vous biaisez toute mesure de performance (et la compatibilité JIT n’est pas un objectif de Xdebug). Si votre but est de déboguer un comportement lié au cache opcode, faites-le sur un environnement où vous contrôlez explicitement OPcache (et où vous savez ce qui est activé). Référence interne utile pour cadrer OPcache correctement : réglages OPcache recommandés.

Un compromis très utilisé en préprod : laisser Xdebug chargé mais inactif la majorité du temps.

Exemple de stratégie « safe-ish » :

  • FPM : xdebug.start_with_request=trigger (pas de debug sans action explicite),
  • pas de discover_client_host en environnement exposé (ou alors strictement maîtrisé),
  • log Xdebug désactivé hors phase de diagnostic,
  • déclenchement via cookie seulement depuis une IP de VPN / bastion.

Table de décision rapide (utile quand on hérite d’une stack existante) :

Besoin Réglage recommandé Pourquoi
Débogage ponctuel web mode=debug,develop + start_with_request=trigger pas de session accidentelle, erreurs lisibles
Composer / scripts rapides mode=off par défaut évite de ralentir tout le monde
Couverture PHPUnit XDEBUG_MODE=coverage sur le job concerné surcharge limitée au pipeline de tests
Mesure perf Xdebug désactivé, outils dédiés mesures non biaisées

Dernier point : sécurité. Un serveur qui accepte des connexions de debug depuis l’extérieur, c’est une porte ouverte si c’est mal cadré (mauvais client_host, IP spoofing via headers si discover_client_host est utilisé naïvement, ou log exposant des chemins). La posture réaliste :

  • Xdebug off par défaut, activation par trigger sur des IP de dev,
  • ne jamais laisser phpinfo() traîner,
  • éviter d’activer xdebug.log en continu (et vérifier les permissions si vous l’utilisez),
  • en prod : ne pas installer Xdebug, ou l’isoler sur un pool FPM interne non routé, derrière une authentification forte et une allowlist réseau.

Si vous devez vraiment l’avoir sur une prod (cas rares, incident critique), faites-le de façon contrôlée : activation temporaire, accès restreint, et retour à l’état nominal dès la fin de l’intervention.


À lire aussi