Skip to content

PHP DBGp

Language: PHP · Transport: stdio

php-dbgp-adapter is Bugsaur's own adapter, built from the same workspace. It replaces the usual DAP↔DBGp bridge and depends on no Node.js, npm, VS Code or vscode-php-debug. At runtime only bugsaur, php-dbgp-adapter and Xdebug are involved.

The catalog entry:

[adapters.php]
id = "php"
command = "php-dbgp-adapter"
transport = "stdio"

[adapters.php.launch_arguments]
port = 9003
sessionMode = "server"

Note that this is the one built-in entry with default launch_arguments: a PHP profile that names neither port nor sessionMode gets 9003 and server.

Install

Nothing external. make build produces the adapter next to bugsaur:

make build
export PATH="$PWD/target/debug:$PATH"
command -v php-dbgp-adapter

Why PHP is structurally different

Xdebug uses DBGp, not DAP, and it works the other way round from the adapters of other languages: Xdebug connects to the debugger, rather than the debugger launching the program.

So php-dbgp-adapter opens a port and waits. Whoever starts PHP — PHP-FPM, Docker, a CLI invocation — is outside Bugsaur's control.

Two consequences:

  • cwd, env and args do not apply. There is no process to give them to.
  • Only three keys are read from the DAP body: port, pathMappings, sessionMode — plus testCommand for the test-under-cursor flow.

The three keys

port

The DBGp port the adapter listens on; 9003 by default. It must match xdebug.client_port in the PHP environment.

One port per machine: two projects debugging at once need different ports.

sessionMode

Value Meaning
server PHP is already running (FPM, built-in server, web container); the adapter waits for requests
cli a one-shot script; the adapter must be listening before it starts

pathMappings

Container path to host path:

pathMappings = { "/app" = "${root}" }

Required whenever PHP runs somewhere with different paths — which in practice means any Docker setup. Without it, breakpoints never bind and stops show no source. See Path mappings.

testCommand

For debugging a PHPUnit test under the cursor, the profile supplies the command that runs PHPUnit where PHP lives:

[profiles.docker.launch_arguments]
testCommand = ["docker", "compose", "exec", "-T", "-e", "XDEBUG_TRIGGER=1", "php", "php", "vendor/bin/phpunit"]

It runs after the adapter has started listening, so the listener is up before Xdebug connects back. XDEBUG_TRIGGER=1 is also passed to the external process.

Required Xdebug settings

xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003

client_host is host.docker.internal from a container and localhost when PHP runs on the same machine.

Diagnostics

Symptom Cause Fix
php-dbgp-adapter not found the build directory is not on PATH export it, above
Nothing ever connects the request ran before the listener was up repeat the request after starting the session
Nothing ever connects, ever xdebug.mode is not debug, or start_with_request is off fix the ini and restart PHP
Port already in use another project holds it give each project its own port
Stops without source pathMappings missing or wrong Source not found
Breakpoints stay pending before the first request nobody to ask yet normal — send a request