Table des matières :
- Prérequis côté stack locale (Apache/PHP-FPM, Docker, DDEV, WSL)
- Installer Xdebug 3 proprement et vérifier qu’il est chargé
- Paramétrage Xdebug 3 : modes, déclenchement, réseau (le vrai sujet)
- VS Code : extension, launch.json, écoute, et mapping de chemins
- Déboguer PrestaShop efficacement : front, back-office Symfony, hooks, AJAX, CLI
- Quand les breakpoints ne prennent pas : checklist réseau, logs Xdebug, OPcache, et risques sécurité
Configurer Xdebug dans VS Code n’est pas compliqué, mais le moindre détail réseau (port, host, mapping de chemins) suffit à rendre le débogage inutilisable. Les exemples ci-dessous visent des stacks courantes PrestaShop 8.1 / 9.1 sur PHP 8.2–8.4 avec Xdebug 3.x (nomenclature Xdebug 3).
Avant d’entrer dans la config, gardez ce modèle mental simple :
- VS Code = le client de debug (il écoute sur un port).
- PHP + Xdebug = le serveur de debug (il initie la connexion vers VS Code).
- Si Xdebug se connecte mais que vos breakpoints restent gris : c’est quasi toujours un problème de mapping de chemins ou de fichier réellement exécuté (cache, OPcache, mauvais container, mauvais PHP CLI).
Prérequis côté stack locale (Apache/PHP-FPM, Docker, DDEV, WSL)
Le point qui conditionne tout, c’est où s’exécute PHP par rapport à VS Code. Si PHP tourne sur la même machine (Linux/macOS/Windows natif), VS Code écoute en local et Xdebug se connecte en général à 127.0.0.1:9003. Si PHP tourne dans un conteneur Docker (ou une VM type WSL2), Xdebug doit joindre l’IP « hôte » (celle où VS Code écoute), et vous devrez presque toujours gérer du path mapping.
Tableau de décision rapide (à relire quand “ça ne marche pas”)
| Votre code PHP s’exécute… | VS Code s’exécute… | xdebug.client_host (souvent) |
pathMappings |
|---|---|---|---|
| sur l’OS hôte | sur l’OS hôte | 127.0.0.1 |
rarement |
| dans Docker (macOS/Windows) | sur l’OS hôte | host.docker.internal |
oui |
| dans Docker (Linux) | sur l’OS hôte | host.docker.internal + extra_hosts: host-gateway |
oui |
| dans WSL2, VS Code côté Windows | Windows (sans Remote) | IP WSL ↔ Windows (volatile) | oui (souvent pénible) |
| dans WSL2, VS Code Remote – WSL | WSL (Remote) | 127.0.0.1 (dans WSL) |
souvent non |
Sur WSL2, l’approche la plus stable est généralement d’utiliser VS Code Remote – WSL (VS Code “tourne” côté WSL), ce qui vous évite les IP changeantes et beaucoup d’astuces réseau.
Dans l’écosystème PrestaShop, la stack locale la plus stable aujourd’hui reste un environnement conteneurisé (Docker) ou DDEV. Si vous utilisez DDEV, gardez en tête que vous exécutez souvent Composer et Symfony Console dans le conteneur (voir : DDEV : outils développeur intégrés, ddev exec/ssh et extensions d’image et DDEV Composer : exécuter et configurer Composer dans les conteneurs). Ce détail influence directement la façon de déboguer du CLI PHP (commands Symfony / scripts) : votre terminal peut viser le PHP du conteneur… ou celui de votre host, et ce n’est pas du tout la même runtime.
Enfin, vérifiez la compatibilité de votre couple PrestaShop/PHP avant de « blâmer » Xdebug. PrestaShop 9.x est aligné Symfony 6.4 et vise une plage PHP large (voir : PrestaShop 9.1 : compatibilité PHP 8.1–8.5, CLI et nouveautés développeurs). Déboguer une boutique qui crash à cause d’une extension PHP manquante, d’un memory_limit trop bas, ou d’un mismatch PHP, c’est perdre du temps : commencez par stabiliser l’exécution (page blanche, HTTP 500, logs PHP/Symfony lisibles).
Installer Xdebug 3 proprement et vérifier qu’il est chargé
Sur Linux, l’installation la plus « propre » est soit via packages distro (apt install php-xdebug), soit via PECL (pecl install xdebug) selon votre gestion de versions PHP. Sur macOS (Homebrew + PHP), idem : installez l’extension compatible avec votre binaire PHP. Sur Windows, vous utilisez souvent une distribution PHP empaquetée (XAMPP/WAMP) ou WSL2 : l’extension doit correspondre exactement à la version de PHP et au mode (TS/NTS) — ne mélangez pas.
Dans Docker, deux approches dominent :
1) image PHP officielle + compilation via pecl install xdebug ;
2) image déjà équipée (certaines images dev) mais avec des réglages par défaut rarement adaptés.
Le piège récurrent : installer Xdebug mais oublier d’activer l’ini (ou l’activer dans un .ini non chargé). Sur les images Debian/Alpine, vérifiez php --ini et le répertoire conf.d.
Autre point qui surprend souvent : CLI et FPM peuvent charger des configs différentes. Vous pouvez avoir Xdebug disponible en CLI (vos php -v le montre) mais absent côté FPM (vos requêtes HTTP ne déclenchent rien), ou l’inverse.
Vérification minimale (à faire dans le même contexte d’exécution que votre code : conteneur, VM, host) :
php -v
php -m | grep -i xdebug || true
php --ri xdebug | sed -n '1,160p'
Si vous êtes en HTTP, un phpinfo() peut aider, mais php --ri xdebug est plus direct. Xdebug fournit aussi une page d’information dédiée via xdebug_info(); (plus lisible que phpinfo pour Xdebug).
Bon réflexe en environnement conteneurisé : faites ces commandes dans le même conteneur que celui qui exécute votre PHP-FPM/Apache (ex. ddev ssh, docker exec -it php bash). Et, si vous avez plusieurs versions PHP installées, vérifiez bien quel binaire vous invoquez réellement :
which php
php --ini | sed -n '1,80p'
Enfin, la documentation Xdebug rappelle explicitement que l’extension a un impact sur les performances et qu’elle ne doit pas rester activée sur des environnements exposés (prod/staging publics) : Xdebug docs
Paramétrage Xdebug 3 : modes, déclenchement, réseau (le vrai sujet)
Xdebug 3 a clarifié la configuration : on active des modes via xdebug.mode. Pour le débogage interactif, le mode indispensable est debug; en pratique, on ajoute souvent develop pour avoir de meilleures traces/diagnostics en dev.
Exemple de configuration (fichier dédié recommandé : 99-xdebug.ini pour éviter les overrides silencieux) :
zend_extension=xdebug
; Modes : debug = step debugger, develop = stack traces améliorées
xdebug.mode=debug,develop
; Démarrage :
; - yes : démarre sur chaque requête (simple, mais plus coûteux)
; - trigger : démarre seulement si XDEBUG_TRIGGER est présent (plus propre)
xdebug.start_with_request=trigger
; Port par défaut Xdebug 3 (doc Xdebug)
xdebug.client_port=9003
; Host côté IDE (varie selon Docker/WSL)
xdebug.client_host=127.0.0.1
; Logs utiles pour diagnostiquer (à désactiver une fois OK)
xdebug.log=/tmp/xdebug.log
xdebug.log_level=7
Référence utile (port et réglages) : page des settings Xdebug, notamment xdebug.client_port et xdebug.start_with_request : Xdebug all settings
Sur une machine locale « simple », xdebug.client_host=127.0.0.1 suffit. En conteneur Docker, vous devrez généralement mettre host.docker.internal (macOS/Windows) ou déclarer une entrée extra_hosts (Linux) pour joindre l’hôte.
Alternative : xdebug.discover_client_host=1 (Xdebug essaie de détecter l’IP client via des headers). C’est tentant, mais souvent fragile dès que vous avez un reverse-proxy, un front HTTP différent, ou des appels internes (CLI, workers). Pour du debug reproductible sur PrestaShop (front, back-office, API), évitez l’auto-détection dès que vous sortez du cas trivial.
Déclenchement “propre” : trigger en HTTP et en CLI
Avec start_with_request=trigger, vous activez le debug uniquement quand vous en avez besoin (et vous évitez de ralentir toutes les requêtes).
- En HTTP : ajoutez un query param
XDEBUG_TRIGGER=1(ou définissez un cookieXDEBUG_TRIGGER=1). - En CLI : exportez
XDEBUG_TRIGGER=1.
export XDEBUG_TRIGGER=1
php bin/console prestashop:some-command -vvv
Mini-scenario très courant en PrestaShop : vous avez un bug uniquement pendant la génération d’un flux, d’un import, ou d’un recalcul (qui passe par CLI). Avec trigger, vous évitez de mettre Xdebug en “always on”, et vous ciblez seulement la commande incriminée.
À propos des logs Xdebug
Le log (xdebug.log) est un outil de diagnostic, pas un réglage permanent. Une fois le réseau stable, baissez le niveau (xdebug.log_level=0) ou commentez le log pour éviter :
- de remplir
/tmp(ou votre volume), - de garder des traces inutiles sur des environnements partagés.
VS Code : extension, launch.json, écoute, et mapping de chemins
VS Code ne parle pas Xdebug nativement : il faut l’extension PHP Debug (xdebug.php-debug). L’extension ouvre un port en écoute (9003) et attend que Xdebug initie la connexion (c’est toujours le serveur PHP qui se connecte au client debug). Page officielle : PHP Debug extension page
Configuration type (workspace .vscode/launch.json) pour écouter :
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"log": false
}
]
}
Deux détails pratiques côté VS Code qui évitent des “faux négatifs” :
- Vérifiez que vous avez bien cliqué sur Run and Debug → Listen for Xdebug avant de déclencher la requête HTTP/CLI.
- Si vous avez un autre service qui écoute déjà sur 9003, changez le port des deux côtés (VS Code +
xdebug.client_port) au lieu d’insister.
Là où ça se complique : le mapping de chemins quand PHP tourne ailleurs que VS Code (Docker, WSL2, VM). Exemple Docker classique : le code est dans le conteneur sous /var/www/html, mais dans votre machine sous /Users/me/projects/shop.
{
"name": "Listen (Docker)",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html": "${workspaceFolder}"
}
}
Sans pathMappings, VS Code reçoit bien la connexion mais ne peut pas associer /var/www/html/classes/... à un fichier local, donc vos breakpoints restent « non résolus ». C’est la cause n°1 des « Xdebug marche mais s’arrête jamais ».
Deux astuces pour fiabiliser le mapping sur des projets PrestaShop réels (pas “Hello world”) :
- Mappez la racine réellement montée, pas une racine “théorique”. Dans Docker/DDEV, vérifiez le chemin avec
pwddans le conteneur au moment où PHP s’exécute. - Si vous avez des montages multiples (ex.
vendor/sur un volume séparé, ou unmodules/monté différemment), un mapping unique peut ne pas suffire. Dans ce cas, alignez la structure de montage, ou ajoutez des mappings plus spécifiques (tout en gardant une structure simple).
Déboguer PrestaShop efficacement : front, back-office Symfony, hooks, AJAX, CLI
Le cœur PrestaShop mélange encore du legacy (classes, controllers historiques) et du Symfony (back-office moderne). Le débogage doit couvrir ces deux mondes. Sur un projet PrestaShop 9.x, vous aurez typiquement :
- front-office : contrôleurs legacy + modules + overrides ;
- back-office : contrôleurs Symfony + services + Twig ;
- endpoints AJAX : souvent hit via
index.php?fc=module...ou routes Symfony.
Quand vous posez des breakpoints dans un module, commencez par un point d’entrée déterministe : un hook très fréquent (actionFrontControllerSetMedia, displayHeader, actionDispatcher, etc.) ou une action Symfony explicite. Si vous développez un module « moderne » (services Symfony, controllers), alignez-vous sur la structure recommandée (voir : Module PrestaShop 9 : structure, services et bonnes pratiques Symfony). Ça réduit les « chemins morts » où vous croyez être exécuté alors que vous ne l’êtes pas (override non chargé, hook non greffé, contrôleur non routé, etc.).
Mini-scenario (concret) : comprendre où ça passe, puis debugger
- Vous suspectez un bug sur l’ajout au panier dans un module.
- Au lieu de mettre un breakpoint “au hasard”, placez-en un dans un hook de passage fréquent côté front (par ex.
actionDispatcher) et un dans la méthode du module qui traite l’action. - Déclenchez une URL avec
?XDEBUG_TRIGGER=1. - Si le hook s’arrête mais pas votre méthode : votre code n’est probablement pas appelé (condition, route, configuration du module, contexte multiboutique, etc.). Vous gagnez du temps car vous n’êtes pas en train de “debugger du vide”.
AJAX : attention aux appels parallèles
Le front PrestaShop (et de nombreux modules) déclenchent des requêtes AJAX en parallèle. En pratique :
- vous activez le trigger,
- plusieurs requêtes partent,
- vous êtes stoppé dans une requête qui n’est pas celle que vous “regardiez”.
Deux stratégies simples :
- déclenchez l’action en réduisant les interactions (un clic, une page simple) ;
- ou ciblez l’endpoint (en reproduisant la requête AJAX en isolé via l’onglet Network du navigateur, puis en la rejouant avec le trigger).
Attention aux caches : un breakpoint dans Twig ou dans une classe autoloadée peut être « masqué » par :
- cache Symfony (var/cache/*) ;
- cache PrestaShop ;
- OPcache (côté FPM) si mal configuré en dev.
Pour une boutique locale, forcez un profil dev cohérent : désactivez/ajustez OPcache en dev, ou au minimum activez la revalidation agressive (opcache.validate_timestamps=1, opcache.revalidate_freq=0).
Pour déboguer du CLI (cron, imports, commandes), ce n’est pas VS Code qui lance PHP par magie : vous lancez votre script avec XDEBUG_TRIGGER=1, VS Code en écoute, et vous vérifiez que le binaire PHP exécuté est bien celui avec Xdebug.
Checklist CLI rapide (quand ça ne s’arrête pas) :
- Est-ce que
php --ri xdebugaffiche bien Xdebug dans ce terminal ? - Est-ce que vous avez bien
export XDEBUG_TRIGGER=1dans le même shell ? - Est-ce que VS Code est en mode écoute au moment du lancement ?
Côté PrestaShop, utilisez la CLI officielle quand possible (voir : Commandes CLI PrestaShop : liste, catégories et options d’aide). En pratique, ça permet de reproduire un bug d’import, de pricing, ou de génération de flux sans dépendre d’un navigateur (donc avec un contexte plus stable et plus scriptable).
Quand les breakpoints ne prennent pas : checklist réseau, logs Xdebug, OPcache, et risques sécurité
Si VS Code n’attrape rien, arrêtez de modifier des paramètres au hasard : observez le flux.
1) Vérifier que VS Code écoute vraiment
Sur Linux, validez que VS Code écoute bien :
ss -lntp | grep 9003 || true
Si rien ne sort, le problème n’est pas Xdebug : c’est le “client” (VS Code) qui n’est pas en écoute, ou un port différent.
2) Vérifier que Xdebug tente une connexion (logs)
Activez le log Xdebug (xdebug.log + xdebug.log_level=7) et relancez une requête avec trigger. Le fichier de log vous aide à distinguer :
- host non résolu (
host.docker.internalintrouvable), - port fermé / connexion refusée,
- connexion établie (auquel cas, on passe au mapping).
Dès que c’est bon, désactivez le log (ou baissez le niveau) pour ne pas “polluer” votre environnement.
3) Docker/WSL : le mauvais host (cause ultra fréquente)
Deuxième cause fréquente en Docker/WSL : mauvais host.
Sur Linux Docker, host.docker.internal n’est pas garanti sans configuration ; ajoutez :
services:
php:
extra_hosts:
- "host.docker.internal:host-gateway"
… puis xdebug.client_host=host.docker.internal.
En WSL2, le host Windows et WSL ont des IP différentes selon les boots ; si vous développez dans WSL mais VS Code côté Windows, vous aurez souvent intérêt à utiliser VS Code Remote – WSL (tout s’exécute alors « côté WSL ») plutôt que de bricoler des IP volatiles.
4) Mapping : connexion OK, mais breakpoints gris
Troisième cause : mauvais mapping. Si le chemin vu par PHP n’est pas mappé, le debug se connecte mais les breakpoints restent gris.
Indicateurs typiques :
- VS Code affiche une stack trace vers des chemins du conteneur (
/var/www/html/...) et n’ouvre pas le bon fichier local. - Vos breakpoints affichent “Unbound breakpoint” / non résolus.
Actions correctives :
- Vérifiez le chemin réel côté PHP avec un
dump(__FILE__)/dump(getcwd())au bon endroit. - Corrigez
pathMappingspour que le chemin côté conteneur pointe vers le chemin local exact.
5) OPcache et caches applicatifs : le code exécuté n’est pas celui que vous éditez
Si vous êtes “sûr” d’être au bon endroit mais que l’exécution ne correspond pas :
- videz les caches Symfony/PrestaShop,
- vérifiez OPcache,
- et assurez-vous que vous n’avez pas deux copies de la boutique montées à deux endroits différents (classique quand on a plusieurs volumes Docker ou plusieurs workspaces VS Code).
6) Risques sécurité : ne transformez pas votre debug en porte d’entrée
Enfin, ne négligez pas la surface d’attaque : ouvrir un port de debug sur une interface réseau non maîtrisée est une mauvaise idée (surtout en environnement partagé).
Bonnes pratiques simples :
- limitez l’écoute à votre machine (évitez d’exposer le port Xdebug via
ports:en Docker si inutile) ; - gardez
start_with_request=triggerpour éviter une “session de debug” ouverte en permanence ; - désactivez Xdebug hors dev (fichier ini non monté, variable d’environnement, ou image dédiée).
Pour la gestion des erreurs et l’hygiène « dev vs prod » (display_errors, logging, niveaux), recoupez avec : Gestion d’erreur PHP : bonnes pratiques et configuration développement/production. Dans PrestaShop, les bugs « fantômes » viennent souvent d’un mix toxique : erreurs masquées + cache + OPcache + debug à moitié activé.
