XDebug, un vieil ami
Publié le
7 avr. 2025

Introduction
C’est l’histoire d’un vieil ami… Souvent décrié, mais je vous jure, il est vraiment sympathique. Depuis plus de 15 ans, je code en pair avec celui-ci, et non, ce n’est pas une IA. Je n’ai pas fait un projet sans lui, il m’a aidé à comprendre, corriger, optimiser mon code depuis tant de temps. Il m’a même sorti de situation incompréhensible sur laquelle j’étais bloqué depuis plusieurs jours. Il n’était pas sans défaut, souvent considéré comme trop lent pour mes collègues, voire impossible de travailler avec lui. On peut dire que c’était un grand incompris. Heureusement, depuis sa version 3, il semble beaucoup fréquentable.Vous l’avez compris, je vous parle d’XDebug (le titre de l’article aide pas mal).
XDebug permet de faire tout un ensemble de choses, profiling, debugging, code coverage. Il reste pour moi un outil indispensable au développeur PHP.
Combien de fois avez-vous mis des dump(‘toto’) ou des var_dump(‘titi’) dans votre code puis rafraichie votre navigateur ou relancer votre commande ? Voir pire, parsemer votre code de var_dump(‘1’), var_dump(‘2’) entre chaque condition ou ligne de votre code ? Ou simplement dumpé une variable pour au final se rendre compte que l’erreur était ailleurs ? Si vous vous êtes reconnu dans les phrases précédentes, je vous en prie arrêtez votre massacre, utilisez XDebug
Lorsque l’on parle XDebug, vous allez entendre les plus vieux râler… Souvent comme ceci :
- C’est galère à mettre en place Spoiler : Franchement pas tant que ça, mais continuez votre lecture 😏
- Avec docker, c’est impossible Spoiler : Mais si, on va vous expliquer ci-dessous
- Ça alourdit l'exécution du code, c’est impossible de travailler avec Spoiler : plus depuis la version 3 🐌
Fonctionnement d’XDebug
Le fonctionnement d’XDebug repose sur 2 parties. L’une est un module PHP et l’autre sur un serveur généralement lancé par votre IDE. La communication entre les deux s’effectue avec le protocole DBGP. Si vous aimez la documentation, elle est ici https://xdebug.org/docs/dbgp
Il permet principalement de piloter l'exécution du code distant (via le module PHP) et permet la récupération des informations (variable, état de l'exécution du code). Le protocole de communication passe par les grandes étapes suivantes :
- Initialisation de la connexion
- L’IDE envoie l’ensemble des conditions d'arrêts demandé par l’utilisateur.
- Puis lorsque le code atteint un breakpoint (point d'arrêt) on rentre dans la boucle d’échange suivante :
- Échange permettant de contrôler l'exécution du code
- Récupération des informations
Avant la version 3, avoir simplement l’extension chargée dans PHP engendrait une sévère dégradation des performances. Ce qui rendait le travail difficile avec d'autres développeurs qui n'utilisaient pas cet outil.
Source : https://php.watch/articles/xdebug2-vs-3-benchmark
Alors rien de bloquant en soit, une simple modification de la configuration PHP et voilà, on active ou désactive notre cher XDebug, mais lorsqu’on travaille dans un environnement “packagé” vagrant pour les plus vieux ou maintenant docker, on arrivait vite à se dire que c’était trop chiant de bosser avec. Avec la version 3, l’impact d’avoir XDebug chargé dans PHP en mode “off” est vraiment minime.
XDebug, les différents modes
C’est bien beau tout cela, mais à quoi ça sert concrètement XDebug ?C’est un “multi tools” qui permet de faire plein de choses différentes, ici un petit tour d’horizon des possibilités :
Trace & Profile
Ces deux modes permettent une analyse de la performance de l'exécution de votre code. Il y a cependant quelques subtilités entre les deux.
C’est un mode pratique en cas d’analyse des performances même si j’ai tendance à préférer la solution de blackfire pour sa lisibilité et leurs suggestions. Plus d’information sur blackfire ici : https://docs.blackfire.io/introduction
Develop
Ce mode permet d’améliorer l’affichage d'informations relatives au débug de votre application. Par exemple, plutôt que de simplement vous donner un message de warning, il vous permettra de dire d'où il provient.
Aussi, il permet d’afficher de manière plus lisible vos données sortie avec un var_dump
Je n’ai personnellement jamais utilisé ce mode, on lui préférera son copain debug Plus d’information ici : https://xdebug.org/docs/develop
Debug
C’est bien ce mode qui est votre meilleur ami. Aussi bien lors de la phase de debug que lors de la phase de développement de votre projet. C’est grâce à celui-ci que l’on va pouvoir piloter l'exécution du code via notre IDE
Mais je vais pouvoir vous détailler cela dans la partie suivante Utilisation
Coverage
Il existe plusieurs solutions permettant de faire du code coverage lors de l'exécution de vos tests. Xdebug est l'une d'entre elles, mais l’on peut aussi citer Pcov ou phpdbg. Je ne vais pas m’étendre sur le sujet, car je n’ai pas replongé dedans depuis la sortie de XDebug 3. Ce que je peux vous dire, c’est que XDebug est souvent considérée comme plus lente que les autres solutions. Mais que personnellement, j’ai trouvé sa mise en œuvre bien plus simple. Lien suggéré pour plus d’informations :https://thephp.cc/articles/pcov-or-xdebug?ref=phpunit
GCstats
Je n’ai personnellement jamais utilisé ce mode, il permet de suivre l’activité du garbage collector de PHP. Je ne peux pas vous en dire grand chose d’autre que ce que donne la documentation officielle.
Plus d’information ici : https://xdebug.org/docs/garbage_collection
Mise en place avec docker
Pour mettre en place XDebug rien de plus simple. Il faut respecter les étapes suivantes :
- Avoir PHP avec le module XDebug (suivant votre version, une compilation pourra être nécessaire.)
- Avoir une interface permettant de piloter XDebug c’est possible de Vim à VSCode, PhpStorm, etc…
- Configurer correctement votre serveur XDebug ainsi que votre mode.
Je pense que la majorité d’entre vous, utilise docker. Si ce n’est pas le cas, pas d’inquiétude, oubliez simplement les parties spécifiques à docker et vous avez les grandes lignes nécessaires. Aussi, la documentation officielle couvre assez largement l’installation hors docker : https://xdebug.org/docs/install
Mise en place du module
Si vous n’avez pas XDebug dans votre noyau PHP, il sera possible de l’installer avec pecl. Il suffira ensuite de le charger dans la configuration PHP.
RUN pecl install xdebug-3.4.0 && \
docker-php-ext-enable xdebug && \
rm -rf /tmp/pear
⚠️ Disclaimer : Même si XDebug en mode off n’engendre que peu d’overhead, je vous déconseille fortement de l’utiliser dans vos builds de productions. On privilégiera l’utilisation des stage dev / prod pour une meilleure séparation.
Configuration du fichier xdebug.ini
xdebug.client_port=9003
xdebug.idekey=IDEKEY
xdebug.mode=debug
xdebug.client_host=host.docker.internal
xdebug.start_with_request=yes
Alors détaillons ici les paramètres que nous donnons à xdebug.
Il ne nous reste que la partie docker, voici un docker-compose.yml
version: '3.8'
services:
php: build:
context: .
dockerfile: docker/php/Dockerfile
volumes: - './docker/php/config/xdebug.ini:/usr/local/etc/php/conf.d/debug.ini'
extra_hosts:
- host.docker.internal:host-gateway
Pour ceux qui font du docker, rien de bien méchant.
La première commande (build) permet de définir ou se situe notre fichier de build docker (le Dockerfile) et depuis quel context (en l'occurrence la racine de l'exécution de la commande docker)
Ici avec la configuration des volumes, nous allons monter le fichier xdebug.ini dans le répertoire de chargement de configuration php. Attention, celui-ci peut changer suivant votre build php.
Et avec l’extra_hosts nous ajoutons dans docker un DNS pointant vers l'hôte du container en l'occurrence notre PC de développement.
Nous avons maintenant notre module prêt à envoyer les informations à l'hôte permettant de piloter l'exécution de notre code à l’adresse host.docker.internal sur le port 9003 avec l’idekey IDEKEY dès que PHP démarre.
Configuration du serveur
Maintenant côté “IDE”.
Étant évangéliste non-officiel de PHPStorm, je vais dans la suite de l’article utiliser des captures d’écrans sur la configuration de XDebug dans celui-ci, cependant, vous pouvez facilement faire un parallèle avec votre IDE préféré. Je ne suis pas vache, on parle un peu aussi de VSCode.
Menu > Run > Edits configurations
Ici, nous allons donner un nom (peu importe quoi), filtrer par IDEKey (optionnelle normalement) puis cliquer sur les … à côté de Server.
Vous allez avoir la fenêtre suivante :
Idem, vous pouvez mettre le nom que vous voulez. En Host, vous pouvez écouter sur l’ensemble des interfaces réseaux 0.0.0.0, le port précédemment configuré 9003 et le type de débugger Xdebug
Vous remarquez ici que nous avons la nécessité de faire une relation entre les fichiers sur le serveur distant (notre container) et ceux en local. Il est donc nécessaire de cocher l’option et de renseigner le path correspondant. Ici dans mes fichiers projets, la racine de mon projet php est dans apps/back, hors sur mon container le code s'exécute dans /usr/src/app, je dois donc faire le lien entre les deux.
Pour VSCode, les mêmes principes s’appliquent. Vous créez une configuration de débug, puis vous renseigner les informations nécessaires. Ici un exemple de fichier de configuration :
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/usr/src/app": "${workspaceFolder}"
}
}
]
}
Bien maintenant nous sommes prêts à débugger notre application.
💡Si vous voulez, vous pouvez lancer votre serveur et vérifier qu’il écoute sur le bon port, la commande netstat est votre ami.
pbrun@pop-os: netstat -natp | grep 9003
tcp6 0 0 :::9003 :::* LISTEN 106909/phpstorm
Utilisation
Je vous invite si ce n’est pas déjà fait, à relancer rebuild votre docker et relancer vos containers.
Ensuite, vous pouvez aller sur l'interface de votre site en développement et observer la différence de performance.
Voici les étapes pour lancer un débug :
- Lancer votre serveur précédemment configuré. Vous pouvez remarquer ici que dans Thread & Variables, le serveur est en attente de connexion.
- Mettre un point d'arrêt (la pastille rouge appelé aussi breakpoint) sur la partie de code que vous souhaitez déboguer. ⚠️ Il n’est pas possible de mettre des points d’arrêt partout, par exemple, il n’est pas possible d’en mettre au beau milieu d’un tableau.
- Il ne vous reste plus qu'à rafraîchir la page de votre projet, et normalement l'exécution de votre code s'arrêtera et vous pouvez piloter celui-ci. Une fois celui arrêté, vous pouvez consulter l’ensemble des variables disponibles, mais aussi la callstack. Plus d'informations disponibles ci-dessous.
Comment l’utiliser ?
Voici votre interface graphique permettant de naviguer dans l'exécution du code. Ça semble logique, mais il ne sera malheureusement pas possible de revenir en arrière dans celui-ci. Si vous dépassez l’endroit qui vous intéresse, vous pouvez relancer :)
Tips
- Imaginons que vous ayez un souci sur une requête Doctrine. Il est possible d'exécuter directement une commande dans le bon contexte. Vous allez pouvoir mettre un point d'arrêt dans votre méthode de repository. Une fois l'exécution stoppée, vous pouvez modifier la requête et l'exécuter localement autant de fois que nécessaire ou simplement afficher son SQL
- Avec un clique droit sur le point d'arrêt (pastille rouge), il est possible de définir des conditions d'arrêt !
- Sans redémarrer votre XDebug, il est possible d’activer celui-ci en ligne de commande php -dxdebug.mode=debug bin/console app:my:commande
- Avec un clique droit sur la pastille rouge, il est possible de simplement désactiver temporairement un breakpoint.
- N’oubliez pas qu’il est possible d’utiliser le mode trigger pour activer ou non le debugging de votre application
- XDebug est l'outil idéal pour comprendre le fonctionnement d’un Framework ou d’une partie de celui-ci !
Voilà, maintenant, je vous en conjure, configurez XDebug sur votre projet. Débugger, explorer votre code ou celui de vos vendors, déboguer efficacement sans rafraîchir en permanence votre navigateur.
Commentaires