Connexion
FR

Déboguer avec Xdebug ou PHP Debugger, à la main ou avec une IA

L'IDE JumPHP a un vrai débogueur pas à pas : on arrête le programme sur une ligne, on regarde les variables, on en change une, on avance pas à pas. Il parle DBGp, le protocole de Xdebug, et peut donc piloter deux moteurs :

  • Xdebug, le classique (par défaut) ;
  • PHP Debugger, un débogueur DBGp plus récent, libre et open source, compatible avec les réglages de Xdebug, au surcoût quasi nul.

Les deux se manient de la même façon dans l'IDE. Vous pouvez aussi confier le débogueur à une IA (Claude Code) : elle pose les points d'arrêt, lance le programme, lit les variables et trouve le bogue toute seule.

Quel moteur choisir

Xdebug PHP Debugger
Dans JumPHP par défaut, tous les runtimes runtime desktop (PHP 8.2 à 8.5)
Surcoût PHP tourne nettement plus lentement « near-zero overhead when idle », toujours actif, aucun déclencheur à régler
Runtime async (PHP 8.6, runtime: async) oui non : Xdebug est utilisé à la place (bin/jumphp le dit)
IA le serveur MCP de JumPHP (bin/jumphp debug) son propre serveur MCP (dbgp mcp), qui voit aussi la sortie du script et les avertissements PHP

En pratique : PHP Debugger pour le quotidien, les longues exécutions (un jeu qui joue 600 images, une suite de tests) et le débogage par IA. Xdebug si vous travaillez avec le runtime async (threads), ou si vous avez besoin de ce que seul Xdebug fait. Les deux sont interchangeables dans l'IDE : essayez-les sur le même projet.

La page de PHP Debugger ne donne aucun chiffre de benchmark : elle affirme seulement un coût quasi nul tant qu'on ne débogue pas. Mesurez sur votre projet si l'écart compte pour vous.

Choisir le moteur dans l'IDE

  • Le bouton Debug de l'en-tête (le bogue à côté de Run, Maj+F9) déroule un menu : Déboguer avec (Xdebug ou PHP Debugger, pour ce lancement) et Moteur par défaut (celui qu'un simple clic utilisera ensuite). Un clic droit sur le bouton ouvre le même menu.
  • Le premier lancement avec un moteur construit une image Docker une fois (environ une minute). Les suivants démarrent aussitôt.
  • Une notification vous prévient quand le programme ne se connecte jamais au débogueur (il s'est terminé avant, ou rien après 3 minutes). La sortie de la fenêtre Run en dit souvent la raison.

Choisir le moteur depuis un terminal

Tout passe par bin/jumphp, comme toujours :

# Xdebug (par défaut)
JUMPHP_XDEBUG=9003 bin/jumphp test pong

# PHP Debugger
JUMPHP_DEBUGGER=php-debugger JUMPHP_XDEBUG=9003 bin/jumphp test pong
  • JUMPHP_XDEBUG=<port> active le pas à pas : PHP se connecte à un débogueur qui écoute sur ce port de votre ordinateur (composer, phpstan et php -l tournent sans lui).
  • JUMPHP_DEBUGGER=xdebug|php-debugger choisit le moteur (xdebug par défaut).
  • Avec php-debugger, bin/jumphp construit une fois l'image docker/desktop-phpdebugger, à partir de l'image desktop, qui reste sans lui.
  • Sur le runtime async (PHP 8.6), le message « PHP Debugger does not run on PHP 8.6 (async runtime): Xdebug is used » s'affiche et Xdebug tourne.

Déboguer à la main dans l'IDE

  1. Point d'arrêt : cliquez dans la gouttière, à gauche d'une ligne PHP. Un clic droit donne une condition (une expression PHP : il ne s'arrête que si elle est vraie, par ex. $this->score < 0) ; Ctrl+F8 en pose un sous le curseur. Les points d'arrêt sont gardés par projet et suivent leurs lignes quand vous éditez.
  2. Debug (Maj+F9) : le programme tourne et s'arrête au premier point d'arrêt. La ligne est marquée et la fenêtre Debug s'ouvre.
  3. Regarder : la pile d'appels (cliquez un cadre pour voir ses variables), les variables en arbre lues à la demande, les espions (watches), et un champ pour évaluer n'importe quelle expression PHP là où le programme s'est arrêté.
  4. Changer une valeur : double clic sur une variable ou F2, tapez une expression PHP (9, "Ada", [1, 2], null) et continuez avec la nouvelle valeur.
  5. Avancer : Reprendre F9, Pas à pas principal F8, Pas à pas détaillé F7, Pas à pas sortant Maj+F8, Exécuter jusqu'au curseur Alt+F9, Arrêter.
  6. Exceptions : S'arrêter aux exceptions… (l'icône d'avertissement de la fenêtre Debug) : * ou des noms de classes (TypeError, App\GameOver). Il s'arrête là où l'exception est levée, attrapée ou non, et indique laquelle avec son message.

Inspecteur au survol (dans la prochaine mise à jour de l'IDE) : quand le programme est arrêté sur un point d'arrêt, passez la souris sur une variable dans l'éditeur pour obtenir un arbre déroulable de sa valeur, avec Copier et Modifier.

Un conseil pour les jeux : déboguez des exécutions sans fenêtre (test, shot) : un jeu arrêté dans une fenêtre se fige jusqu'à ce que vous reprenniez.

Déboguer avec une IA

Une IA débogue mieux avec un débogueur qu'avec des var_dump() : elle lit les vraies valeurs, une question à la fois, sans rien changer à votre code. Deux serveurs MCP existent. N'en utilisez qu'un à la fois (les deux écoutent sur le port 9003).

1. Le serveur de JumPHP : bin/jumphp debug

Le débogueur de l'IDE lui-même comme serveur MCP (il tourne dans Docker, avec Xdebug) :

claude mcp add jumphp-debug -- bin/jumphp debug

Ses outils :

Outil Ce qu'il fait
debug_breakpoints pose les points d'arrêt d'un fichier (lines, ou {line, condition}), les liste sans chemin, clear_all
debug_exceptions les exceptions qui arrêtent le programme (* = toutes, [] = aucune)
debug_wait attend que le programme s'arrête sur un point d'arrêt ou une exception, puis donne où, la pile et les variables
debug_state où il est arrêté, la pile, les variables d'un cadre
debug_step step_over, step_into, step_out, run, stop : attend l'arrêt suivant
debug_run_to avance jusqu'à une ligne
debug_eval évalue une expression PHP là où il s'est arrêté
debug_variables les variables d'un cadre, ou les enfants de l'une ($this->player, $list[3])
debug_set_value donne une nouvelle valeur à une variable (une expression PHP)

Les chemins sont relatifs au projet. Claude lance le programme lui-même, en arrière-plan, avec JUMPHP_XDEBUG=9003, puis appelle debug_wait.

2. Le serveur de PHP Debugger : dbgp mcp

PHP Debugger fournit son propre serveur MCP dans un petit programme, dbgp (licence MIT ; Linux et macOS, amd64 et arm64 ; Windows amd64).

# installation (macOS / Linux)
curl -fsSL https://github.com/php-debugger/dbgp-client/releases/latest/download/install.sh | sh
dbgp -version

# pour ce projet / pour tous vos projets
claude mcp add dbgp -- dbgp mcp
claude mcp add --scope user dbgp -- dbgp mcp

Dans Claude Code, /mcp montre qu'il est connecté. Ses outils, sous leurs noms de la documentation de PHP Debugger :

  • connexion : status, listen, unlisten, sessions, wait_for_session, detach ;
  • points d'arrêt : add_breakpoint, remove_breakpoint, breakpoints ;
  • exécution : continue, wait_for_stop, stop ;
  • regarder : stack, variables, variable, variable_value, source, output, warnings ;
  • agir : eval, set_variable ;
  • Docker / distant : add_path_mapping, path_mappings.

Options utiles (dbgp mcp -h pour toutes) : -php-port PORT (le port où PHP se connecte, 9003 par défaut), -mcp-port PORT, -idekey KEY, -map LOCAL=DISTANT (une correspondance de chemins, répétable), et -no-eval, qui interdit eval, set_variable et les points d'arrêt conditionnels : l'IA ne peut que regarder. Cela ne cache pas les valeurs : mots de passe et clés restent visibles pour elle.

Par bin/jumphp, JUMPHP_DEBUGGER=php-debugger bin/jumphp debug lance dbgp mcp pour vous (il faut dbgp installé sur votre ordinateur). Lancez ensuite le programme avec le même moteur :

JUMPHP_DEBUGGER=php-debugger JUMPHP_XDEBUG=9003 bin/jumphp test pong

Une session type

Vous, à Claude Code :

Le score du jeu pong monte de 2 au lieu de 1 quand la balle passe la raquette de gauche. Utilise le débogueur, pas var_dump. Pose un point d'arrêt là où le score augmente (cherche dans PlayScene), lance bin/jumphp test pong avec JUMPHP_XDEBUG=9003, arrête-toi dessus, lis les variables et dis-moi pourquoi. Ne corrige qu'après m'avoir montré la cause.

Ce que fait alors l'IA, avec les outils ci-dessus :

  1. trouve la ligne qui ajoute un point et y pose un point d'arrêt, avec une condition si la ligne passe à chaque image ($this->ballX < 0) ;
  2. lance le test en arrière-plan et attend l'arrêt (debug_wait / wait_for_stop) ;
  3. lit $this->score, $this->scored et la pile pour voir qui a appelé cette ligne ;
  4. sort une fois (debug_step step_out) pour voir le second appelant : le point est compté deux fois ;
  5. vérifie son idée sans rien modifier : elle donne une valeur à $this->scored (debug_set_value / set_variable) et continue pour voir le score rester juste ;
  6. seulement alors, elle modifie le code et relance le test, sans point d'arrêt.

Conseils

  • Dites-le clairement : « utilise le débogueur ». Sinon un agent retombe souvent sur var_dump().
  • Donnez-lui la commande qui montre le bogue, ce que vous attendez, et où vous soupçonnez le problème.
  • Petits pas : quelques step_over, puis on regarde. Un point d'arrêt conditionnel ($enemy->hp <= 0) vaut mieux que 600 images pas à pas.
  • Laissez-la lire les variables et évaluer des expressions : pas de var_dump(), pas d'echo, rien à nettoyer ensuite.
  • Préférez les exécutions sans fenêtre (test, shot, php).
  • Secrets : l'IA voit toutes les variables qu'elle lit (mots de passe, jetons, clés d'API). Déboguez avec des données de test, et utilisez -no-eval avec dbgp si vous ne voulez pas qu'elle exécute du code dans votre programme.
  • Ne laissez pas le serveur MCP d'un débogueur connecté dans un projet où vous ne déboguez pas : retirez-le avec claude mcp remove.

Dépannage

Rien ne s'arrête / « did not connect ». Un débogueur écoute-t-il sur le port (la fenêtre Debug de l'IDE, ou un serveur MCP) ? Le programme doit être lancé avec JUMPHP_XDEBUG=<port> et le même moteur que celui qui écoute.

Port déjà utilisé. Deux écouteurs sur 9003 : l'IDE et bin/jumphp debug (ou dbgp mcp), ou deux IDE. Arrêtez-en un, ou prenez un autre port : JUMPHP_DEBUG_PORT=9010 bin/jumphp debug avec JUMPHP_XDEBUG=9010, ou dbgp -php-port 9010 mcp.

Le point d'arrêt n'est jamais atteint. Il est sur une ligne sans code (commentaire, ligne vide, accolade fermante), ou dans un fichier que le programme ne charge jamais. Lancez debug_state / regardez la pile pour voir où il en est. Avec un -map ou un IDE : les chemins doivent correspondre (ci-dessous).

Chemins dans Docker. bin/jumphp monte votre projet dans /workspace du conteneur : le débogueur dit file:///workspace/.... L'IDE et bin/jumphp debug le rapprochent seuls des fichiers du projet, et les chemins que vous donnez sont relatifs au projet. Avec dbgp mcp seul, faites correspondre les dossiers : dbgp mcp -map /home/vous/projet=/workspace.

PHP 8.6 (runtime async). PHP Debugger n'y tourne pas : Xdebug est utilisé. Pour utiliser PHP Debugger, déboguez sur le runtime par défaut (PHP 8.5), ou retirez runtime: async le temps de la session.

Jeux dans le navigateur. Le débogueur travaille sur le runtime desktop. Dans le navigateur, reproduisez le bogue avec bin/jumphp test ou shot sur le bureau.

Voir aussi Coder, lancer et tester, Claude dans l'IDE et IDE Remote.