ES

Depurar con Xdebug o PHP Debugger, a mano o con una IA

El IDE de JumPHP tiene un verdadero depurador paso a paso: se detiene el programa en una línea, se miran las variables, se cambia una y se avanza paso a paso. Habla DBGp, el protocolo de Xdebug, y por eso puede manejar dos motores:

  • Xdebug, el clásico (por defecto);
  • PHP Debugger, un depurador DBGp más reciente, libre y de código abierto, compatible con los ajustes de Xdebug, con una sobrecarga casi nula.

Ambos se manejan igual en el IDE. También puedes confiar el depurador a una IA (Claude Code): pone los puntos de interrupción, ejecuta el programa, lee las variables y encuentra el error ella sola.

Qué motor elegir

Xdebug PHP Debugger
En JumPHP por defecto, todos los runtimes runtime de escritorio (PHP 8.2 a 8.5)
Sobrecarga PHP va claramente más lento «near-zero overhead when idle», siempre activo, sin disparador que configurar
Runtime async (PHP 8.6, runtime: async) sí no: se usa Xdebug en su lugar (bin/jumphp lo dice)
IA el servidor MCP de JumPHP (bin/jumphp debug) su propio servidor MCP (dbgp mcp), que también ve la salida del script y los avisos de PHP

En la práctica: PHP Debugger para el día a día, las ejecuciones largas (un juego que juega 600 fotogramas, una suite de tests) y la depuración con IA. Xdebug si trabajas con el runtime async (hilos) o necesitas algo que solo Xdebug hace. Son intercambiables en el IDE: pruébalos en el mismo proyecto.

La página de PHP Debugger no da ninguna cifra de benchmark: solo afirma un coste casi nulo mientras no depuras. Mide en tu proyecto si la diferencia te importa.

Elegir el motor en el IDE

  • El botón Debug de la cabecera (el bicho junto a Run, Mayús+F9) despliega un menú: Depurar con (Xdebug o PHP Debugger, para esta ejecución) y Motor predeterminado (el que usará un simple clic a partir de entonces). Un clic derecho en el botón abre el mismo menú.
  • La primera ejecución con un motor construye una imagen Docker una sola vez (aprox. un minuto). Las siguientes arrancan al instante.
  • Una notificación avisa cuando el programa nunca se conecta al depurador (terminó antes, o nada tras 3 minutos). La salida de la ventana Run suele decir por qué.

Elegir el motor desde un terminal

Todo pasa por bin/jumphp, como siempre:

# Xdebug (por defecto)
JUMPHP_XDEBUG=9003 bin/jumphp test pong

# PHP Debugger
JUMPHP_DEBUGGER=php-debugger JUMPHP_XDEBUG=9003 bin/jumphp test pong
  • JUMPHP_XDEBUG=<puerto> activa el paso a paso: PHP se conecta a un depurador que escucha en ese puerto de tu ordenador (composer, phpstan y php -l corren sin él).
  • JUMPHP_DEBUGGER=xdebug|php-debugger elige el motor (xdebug por defecto).
  • Con php-debugger, bin/jumphp construye una vez la imagen docker/desktop-phpdebugger, a partir de la imagen de escritorio, que se queda sin él.
  • En el runtime async (PHP 8.6) aparece el mensaje «PHP Debugger does not run on PHP 8.6 (async runtime): Xdebug is used» y corre Xdebug.

Depurar a mano en el IDE

  1. Punto de interrupción: haz clic en el margen, a la izquierda de una línea de PHP. Un clic derecho da una condición (una expresión PHP: solo se detiene si es verdadera, p. ej. $this->score < 0); Ctrl+F8 pone uno bajo el cursor. Los puntos se guardan por proyecto y siguen a sus líneas al editar.
  2. Debug (Mayús+F9): el programa corre y se detiene en el primer punto. La línea queda marcada y se abre la ventana Debug.
  3. Mirar: la pila de llamadas (haz clic en un marco para ver sus variables), las variables en árbol leídas bajo demanda, las vigilancias (watches) y un campo para evaluar cualquier expresión PHP donde el programa se detuvo.
  4. Cambiar un valor: doble clic en una variable o F2, escribe una expresión PHP (9, "Ada", [1, 2], null) y sigue con el nuevo valor.
  5. Avanzar: Reanudar F9, Paso por encima F8, Paso al interior F7, Paso hacia fuera Mayús+F8, Ejecutar hasta el cursor Alt+F9, Detener.
  6. Excepciones: Detener en excepciones… (el icono de aviso de la ventana Debug): * o nombres de clase (TypeError, App\GameOver). Se detiene donde se lanza la excepción, capturada o no, e indica cuál con su mensaje.

Inspector al pasar el ratón (en la próxima actualización del IDE): con el programa detenido en un punto de interrupción, pasa el ratón sobre una variable en el editor para obtener un árbol desplegable de su valor, con Copiar y Editar.

Un consejo para los juegos: depura ejecuciones sin ventana (test, shot): un juego detenido dentro de una ventana se congela hasta que continúes.

Depurar con una IA

Una IA depura mejor con un depurador que con var_dump(): lee los valores reales, una pregunta cada vez, sin tocar tu código. Hay dos servidores MCP. Usa uno a la vez (los dos escuchan en el puerto 9003).

1. El servidor de JumPHP: bin/jumphp debug

El depurador del propio IDE como servidor MCP (corre en Docker, con Xdebug):

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

Sus herramientas:

Herramienta Qué hace
debug_breakpoints pone los puntos de un archivo (lines, o {line, condition}), los lista sin ruta, clear_all
debug_exceptions las excepciones que detienen el programa (* = todas, [] = ninguna)
debug_wait espera a que el programa se detenga en un punto o una excepción, y da dónde, la pila y las variables
debug_state dónde está detenido, la pila, las variables de un marco
debug_step step_over, step_into, step_out, run, stop: espera la siguiente parada
debug_run_to avanza hasta una línea
debug_eval evalúa una expresión PHP donde se detuvo
debug_variables las variables de un marco, o los hijos de una ($this->player, $list[3])
debug_set_value da un nuevo valor a una variable (una expresión PHP)

Las rutas son relativas al proyecto. Claude lanza el programa él mismo, en segundo plano, con JUMPHP_XDEBUG=9003, y luego llama a debug_wait.

2. El servidor de PHP Debugger: dbgp mcp

PHP Debugger trae su propio servidor MCP en un programa pequeño, dbgp (licencia MIT; Linux y macOS, amd64 y arm64; Windows amd64).

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

# para este proyecto / para todos tus proyectos
claude mcp add dbgp -- dbgp mcp
claude mcp add --scope user dbgp -- dbgp mcp

En Claude Code, /mcp muestra que está conectado. Sus herramientas, con los nombres de la documentación de PHP Debugger:

  • conexión: status, listen, unlisten, sessions, wait_for_session, detach;
  • puntos de interrupción: add_breakpoint, remove_breakpoint, breakpoints;
  • ejecución: continue, wait_for_stop, stop;
  • mirar: stack, variables, variable, variable_value, source, output, warnings;
  • actuar: eval, set_variable;
  • Docker / remoto: add_path_mapping, path_mappings.

Opciones útiles (dbgp mcp -h para todas): -php-port PUERTO (el puerto al que se conecta PHP, 9003 por defecto), -mcp-port PUERTO, -idekey CLAVE, -map LOCAL=REMOTO (una correspondencia de rutas, repetible) y -no-eval, que prohíbe eval, set_variable y los puntos condicionales: la IA solo puede mirar. No oculta los valores: contraseñas y claves siguen siendo visibles para ella.

Con bin/jumphp, JUMPHP_DEBUGGER=php-debugger bin/jumphp debug lanza dbgp mcp por ti (necesita dbgp instalado en tu ordenador). Luego lanza el programa con el mismo motor:

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

Una sesión de ejemplo

Tú, a Claude Code:

La puntuación del juego pong sube 2 en vez de 1 cuando la bola pasa la pala izquierda. Usa el depurador, no var_dump. Pon un punto de interrupción donde sube la puntuación (busca en PlayScene), ejecuta bin/jumphp test pong con JUMPHP_XDEBUG=9003, párate ahí, lee las variables y dime por qué. Corrige solo después de enseñarme la causa.

Lo que hace entonces la IA, con las herramientas de arriba:

  1. encuentra la línea que suma un punto y pone ahí un punto de interrupción, con una condición si la línea se ejecuta en cada fotograma ($this->ballX < 0);
  2. lanza el test en segundo plano y espera la parada (debug_wait / wait_for_stop);
  3. lee $this->score, $this->scored y la pila para ver quién llamó a esa línea;
  4. sale una vez (debug_step step_out) para ver al segundo llamante: el punto se cuenta dos veces;
  5. comprueba su idea sin editar nada: da un valor a $this->scored (debug_set_value / set_variable) y continúa para ver que la puntuación queda bien;
  6. solo entonces edita el código y repite el test, sin punto de interrupción.

Consejos

  • Dilo claro: «usa el depurador». Si no, un agente suele volver a var_dump().
  • Dale el comando que muestra el error, lo que esperas y dónde sospechas.
  • Pasos pequeños: unos step_over y mirar. Un punto condicional ($enemy->hp <= 0) vale más que 600 fotogramas paso a paso.
  • Déjala leer variables y evaluar expresiones: sin var_dump(), sin echo, nada que limpiar después.
  • Prefiere las ejecuciones sin ventana (test, shot, php).
  • Secretos: la IA ve todas las variables que lee (contraseñas, tokens, claves de API). Depura con datos de prueba y usa -no-eval con dbgp si no quieres que ejecute código en tu programa.
  • No dejes conectado el servidor MCP de un depurador en un proyecto donde no depuras: quítalo con claude mcp remove.

Solución de problemas

Nada se detiene / «did not connect». ¿Escucha un depurador en el puerto (la ventana Debug del IDE o un servidor MCP)? El programa debe lanzarse con JUMPHP_XDEBUG=<puerto> y el mismo motor que el que escucha.

Puerto ya en uso. Dos oyentes en 9003: el IDE y bin/jumphp debug (o dbgp mcp), o dos IDE. Para uno, o usa otro puerto: JUMPHP_DEBUG_PORT=9010 bin/jumphp debug con JUMPHP_XDEBUG=9010, o dbgp -php-port 9010 mcp.

El punto de interrupción nunca se alcanza. Está en una línea sin código (comentario, línea vacía, llave de cierre) o en un archivo que el programa nunca carga. Usa debug_state / mira la pila para ver dónde está. Con un -map o un IDE: las rutas deben coincidir (abajo).

Rutas en Docker. bin/jumphp monta tu proyecto en /workspace del contenedor: el depurador dice file:///workspace/.... El IDE y bin/jumphp debug lo asocian solos con los archivos del proyecto, y las rutas que das son relativas al proyecto. Con dbgp mcp solo, asocia las carpetas: dbgp mcp -map /home/tu/proyecto=/workspace.

PHP 8.6 (runtime async). PHP Debugger no corre ahí: se usa Xdebug. Para usar PHP Debugger, depura en el runtime por defecto (PHP 8.5) o quita runtime: async durante la sesión.

Juegos en el navegador. El depurador trabaja en el runtime de escritorio. En el navegador, reproduce el error con bin/jumphp test o shot en el escritorio.

Ver también Programar, ejecutar y probar, Claude en el IDE e IDE Remote.