With Xdebug 3, what must you configure to step-debug PHP code, and which side opens the debugging connection?
answer
- default mode is develop
- xdebug.mode=debug
- PHP calls the IDE, not the reverse
- DBGp over TCP port 9003
- client_host defaults to localhost
basics
~10 sSet xdebug.mode=debug, have the IDE listen for DBGp connections on port 9003, and start a session with a trigger; Xdebug, running inside PHP, opens the connection to the IDE at xdebug.client_host:xdebug.client_port.
solid answer
~40 sXdebug's default `xdebug.mode` is `develop`, which only enables the development helpers, so step debugging needs `xdebug.mode=debug` (or a list such as `develop,debug`) in a php.ini file read at process start. The IDE then starts **listening**, and when a request carries a trigger, Xdebug **connects out** to the IDE using the DBGp protocol over TCP, to `xdebug.client_host` (default `localhost`) on `xdebug.client_port` (default `9003`). The IDE never connects into PHP; it waits for PHP to call. With the default `xdebug.start_with_request=default`, debug mode behaves as `trigger`, so nothing happens until a request carries `XDEBUG_TRIGGER` or `XDEBUG_SESSION`, or code calls `xdebug_break()`. When PHP and the IDE run on the same machine, those defaults are all you need.
code
ini · 6 lines; conf.d/99-xdebug.ini
zend_extension=xdebug
xdebug.mode=develop,debug
xdebug.client_host=localhost ; default: IDE on the same machine
xdebug.client_port=9003 ; default since Xdebug 3
xdebug.log=/tmp/xdebug.log ; record connection attemptsgo deeper
Recall that step debugging needs xdebug.mode=debug, that the IDE listens on port 9003, and that Xdebug starts the connection.
Explain why the default mode is develop, why the direction of the connection matters for client_host, and why debug mode waits for a trigger.
Show you can reason about which ini file each SAPI loads, verify the active mode with xdebug_info(), and reach the IDE from remote PHP.
Set team defaults: debugging off unless triggered, a documented shared config, and no Xdebug in production images.
## What step debugging is **Step debugging** means pausing a running PHP script at a breakpoint, inspecting variables, and executing it line by line from an IDE. **Xdebug** is the PHP extension that makes the engine stoppable, and it speaks **DBGp**, an open debugger protocol that most PHP IDEs and editors implement. Two roles are involved: - the **debugger engine**, Xdebug inside the PHP process; - the **debugging client**, the IDE or editor plugin that shows the code and sends "step over" or "evaluate" commands. ## Step 1: turn on debug mode Xdebug 3 enables features through a single setting, `xdebug.mode`. Its default is **`develop`**, which gives you the improved `var_dump()` and error output but **no step debugging**. ```ini ; 99-xdebug.ini (loaded when PHP starts) zend_extension=xdebug xdebug.mode=debug ; or develop,debug to keep the helpers ``` `xdebug.mode` can only be set in files read when the PHP process starts, such as `php.ini` or a `conf.d` file. It is **not** honoured in `.htaccess`, `.user.ini` or a PHP-FPM pool's `php_admin_value`. After changing it, restart PHP-FPM or the web server, because a running worker keeps the value it started with. ## Step 2: understand who connects to whom This is the part that surprises people used to attaching a debugger to a process: 1. The IDE opens a **listening socket**, which is its "start listening for debug connections" button. 2. A request starts in PHP, and Xdebug decides whether to debug it (Step 3). 3. If so, **Xdebug connects out** to `xdebug.client_host:xdebug.client_port`. 4. The IDE accepts, sets its breakpoints over DBGp, and the request runs until it hits one. | Setting | Default | Meaning | |---|---|---| | `xdebug.client_host` | `localhost` | where the IDE is listening | | `xdebug.client_port` | `9003` | the port the IDE listens on | | `xdebug.connect_timeout_ms` | `200` | how long Xdebug waits for the IDE to accept | Because the connection goes **from PHP to the IDE**, the machine running the IDE must accept an incoming connection on 9003. When PHP runs elsewhere, whether a VM, a remote server or a container, `client_host` must be an address **as seen from PHP** that reaches the IDE. ### Where client_host should point | PHP runs on | Typical `xdebug.client_host` | |---|---| | the same machine as the IDE | `localhost` (the default) | | a VM or remote server | the IDE machine's address as seen from that server | | a container on your laptop | a name that reaches the host, such as `host.docker.internal` | | a shared server used by several developers on one network | `xdebug.discover_client_host=1`, which connects back to the requesting address | The last option has no filter: anyone who can reach the web server can make it connect back to them, so it belongs on trusted networks only. ## Step 3: decide which requests are debugged `xdebug.start_with_request` decides when Xdebug tries to connect. Its default value, `default`, means **`trigger`** in debug mode: Xdebug only connects when the request carries a trigger, such as an `XDEBUG_TRIGGER` or `XDEBUG_SESSION` cookie, GET or POST variable, or environment variable, or when the code calls `xdebug_break()`. Setting it to `yes` makes every request try. ## Checking that it works - `xdebug_info()` shows an HTML diagnostics page, like `phpinfo()` for Xdebug, with the active modes, every setting, and any connection errors. - `xdebug_info('mode')` returns the enabled modes as an array, for example `['debug']`. - Setting `xdebug.log=/tmp/xdebug.log` records every connection attempt. ## Common first-time mistakes - Leaving `xdebug.mode` at `develop` and wondering why the IDE never reacts. - Editing the CLI `php.ini` while the web server's PHP-FPM reads a different one; check with `xdebug_info()` from a web request. - Expecting the IDE to connect to PHP, and opening port 9003 on the server instead of the IDE's machine. - Following an Xdebug 2 tutorial that uses `xdebug.remote_enable` and port 9000, which Xdebug 3 no longer uses.
- Why can you not set xdebug.mode in a .user.ini file or with php_admin_value?Xdebug decides which features to hook into the engine when the PHP process starts, so `xdebug.mode` is only read from ini files loaded at startup. Per-directory `.user.ini` files and FPM pool `php_admin_value` entries are applied later, per request or per pool, which is too late for Xdebug to act on them.
- Your IDE is listening, xdebug.mode=debug is set, yet nothing happens on page load. What is the most likely reason?The default start mode for debugging is `trigger`, so Xdebug only connects when the request carries `XDEBUG_TRIGGER` or `XDEBUG_SESSION`, as a cookie, GET/POST variable or environment variable, or when code calls `xdebug_break()`. Without a trigger it stays silent by design. Add the trigger, for example with a browser helper extension.
Step debugging with Xdebug is like a callback service: the IDE sits by its phone (listening on 9003), and PHP places the call when a request asks for it. If the IDE is not by the phone, or PHP dials the wrong number, nothing connects, and the IDE could never have called PHP instead.
saying these in an interview costs you the question
- Installing Xdebug enables step debugging by default
- The IDE connects into the PHP process on port 9003
- xdebug.remote_enable=1 turns on the debugger in Xdebug 3
- Setting xdebug.mode in .user.ini is enough
- Xdebug connects to the IDE on every request by default