Copying the xdebug config lines from the /etc/php5/apache2/php.ini file into /etc/php5/cli/php.ini setting an environment variable with the name of the debug session (you can get this from the query string in the url of the page netbeans launches when you start debugging) so the command is: export XDEBUGCONFIG='idekey=netbeans-xdebug'.
- If this setting is 1, Xdebug will color vardumps and stack traces output when in CLI mode and when the output is a tty. On Windows, the ANSICON tool needs to be installed. If the setting is 2, then Xdebug will always color vardumps and stack trace, no matter whether it's.
- Integer xdebug.clicolor = 0 # Introduced in Xdebug = 2.2. If this setting is 1, Xdebug will color vardumps and stack traces output when in CLI mode and when the output is a tty. On Windows, the ANSICON tool needs to be installed.
So here's my second annual (as in once-per-year) blog post. I hoped this would've happened more often, but oh well.
After many years of Sublime Text usage, I recently switched to VS Code, as the Sublime Text ecosystem for PHP development seems to be somewhat less active lately, based on my personal experience.
I was also recently starting work on a new Laravel project and decided to setup Xdebug so I could, well, debug. Most of the online guides I've found while hoping for a quick copy and paste configuration didn't end up working, and were in fact aimed at Xdebug 2 - whereas the new Xdebug 3 version changed some of the configuration setting keys. I spent a couple of hours getting everything to work nicely, and encountered a weird (but in hindsight, sensible) issue.
'Running in Docker' is not very specific, so I'll start by explaining my project setup. It's a Laravel app with a docker-compose.yml
file in the project root, which looks a bit like this (documentation, unrelated services, and unrelated configuration settings removed for readability):
The HOST_UID
and HOST_GID
are configured to be used by Apache in the web
image and PHP in the php
image so as not to mess up file permissions and ownership on the host machine.
For those curious, I also have the following services as a starting point for most Laravel projects:
- a MySQL service as the app's database
- a queue worker service (based on the same Docker image as the
php
service) for runningartisan queue:work
- a Node service for Node-related stuff (Yarn dependencies and the front-end build process)
- Mailhog for local email testing
- Beanstalk as a messaging queue
- Beanstalk Aurora as a Beanstalk UI
Both the web
and php
services run a similar Docker image in terms of PHP - with the difference being that web
also includes Apache through which the web application is served. I also have a local Nginx setup for proxying requests to various projects so that I can use https://project.localhost
, https://mailhog.project.localhost
etc. instead of having to remember the per-project ports for individual exposed services. For SSL in local development I use mkcert, though I guess none of this is too relevant for today's topic of Xdebug, and I should just make a separate blog post documenting that whole setup if anyone's interested.
Importantly though, the Docker images for running the web-app and CLI commands are based on ubuntu:bionic
- I've been wanting to switch to the php
images but just haven't gotten around to it yet. The Dockerfile
s for the web
and php
images install PHP 7.4 from ppa:ondrej/php
, along with a bunch of extensions including php7.4-xdebug
- which, as of recently, ends up with Xdebug 3.0.1 installed in the image.
This whole guide should work just as well for PHP 8.0 as well as for older versions, though you probably shouldn't be using older versions anyway.
If you have a very specific setup that's similar to mine, you're gonna want to do the following:
Variables order
In my Docker images, PHP's default variables order was set to GPCS
- which is not good because Xdebug looks for an environment variable in $_ENV
when attempting to detect a trigger from a CLI command. Thus, my Dockerfile
copies a file with the following contents to /etc/php/7.4/cli/conf.d/99-variables-order.ini
:
This was giving me trouble and actually took me more time than everything else here to figure out why Xdebug wasn't working for me when attempting to trigger it from the command line.
Xdebug configuration
The Dockerfile
also copies a file with the following contents to /etc/php/7.4/cli/conf.d/99-xdebug.ini
(and /etc/php/7.4/apache2/conf.d/99-xdebug.ini
in the web
image):
The first line configures step debugging, and also affects how some other Xdebug configuration works by default (notably the default value of the xdebug.start_with_request
setting).
The second line tells Xdebug which address to use to connect to the IDE - which is running on the host machine, and host.docker.internal
is a special hostname which resolves to the host machine's IP address.
Note that my Dockerfile
configuration which installs php7.4
and (among others) php7.4-xdebug
from ppa:ondrej/php
using apt
will automatically enable the extension as well, so I don't need to explicitly do that. If you do, you'll want to also add zend_extension=/path/to/xdebug.so
in this file.
host.docker.internal
As a sidenote, this wasn't supported on Linux at all for a long time until this PR was merged - and with it, Linux users still have to explicitly map host.docker.internal
to the magic host-gateway
value, which Docker then resolves to the host machine's IP address when starting the container and saves it in the container's /etc/hosts
file. That's why the extra_hosts
setting is needed in the docker-compose.yml
file - and hopefully this shouldn't cause any issues in non-Linux (ie. Mac or Windows) Docker environments, though I haven't been personally able to test it yet.
VS Code
Xdebug Cli
In VS Code you want to install the felixfbecker.php-debug
extension (VS Code Marketplace, GitHub):
You can follow the VS Code configuration section of that extension's installation instructions, but what worked for my setup was to add the following into my project's project.code-workspace
file:
The extension also suggests adding a 'Launch currently open script' launch configuration, but I don't see how that would be useful in the context of a framework like Laravel, so I skipped it.
As for the other stuff, here's what the configuration settings mean:
name
- just the name of the configuration which will be displayed in the debugger and the taskbartype
- should bephp
to tell VS Code that this configuration should run debugging with the PHP Debug extension we just installedrequest
- should belaunch
as that's appropriate for how we debug PHP with Xdebugport
- should be9003
for Xdebug 3, which is the new default port that Xdebug will connect to - in older versions, the default port vas9000
, but we want to minimize our configuration work, so best to stick with the defaults when we canpathMappings
- this is important because Xdebug is running in the Docker container, where the app's files are under a different path; this is very configuration-specific so adjust it to your own setup, but myDockerfile
s use/opt
as theWORKDIR
and that's the volume I bind my project's directory to - hence my value for this configuration settingignore
- optional paths that errors will be ignored fromxDebugSettings
- consult the extension's documentation for more info, but I needed to set this higher than the default values because the defaults were too small for my use-case
To expand on the last point, one issue I had with the low default xDebugSettings
was that my debugger would show, for example, that $_SERVER
is an array()
with ~60ish items, but when I expanded that variable, it would always only show the first 32 items and nothing else. Increasing these values fixed the problem.
So now we're done with configuring Xdebug and VS Code, so how do we actually debug?
There's two contexts in which it makes sense to debug PHP code - browser requests and CLI commands. Both are explained in Xdebug's documentation, but here's a short summary.
Browser
You want to install one (or more) of the following extensions, depending on which browser you're working with:
- Xdebug Helper for Firefox (source)
- Xdebug Helper for Chrome (source)
- XDebugToggle for Safari (source)
Xdebug Client
Once installed, you'll get an extension icon/button in your browser which enables you to select the Xdebug feature you want to trigger. I'm only interested in debugging, so when I want to do that, I'll click the icon and select the 'debug' mode with the green bug icon.
Then I'll start a debugging session in VS Code by pressing F5 (which should be the default keybinding - if not, you can always open your 'Run' panel in VS Code and click the little 'play' icon next to the dropdown menu where 'Listen for XDebug' should be selected).
I'll add a breakpoint where I need it (by simply clicking next to the line number), and refresh my browser page. What should happen is that Xdebug will connect to VS Code, which will in turn inform Xdebug about the breakpoint which was set, and then while the code is executing, if it reaches that line of code, it will pause execution and you can then step-debug in VS Code. Yay!
Note that depending on how the web server serving your PHP application is configured, you might get timeouts in your browser - but this article won't deal with that as it's already getting too long.
Don't forget to switch the browser extension's Xdebug mode back to 'Disable' (gray bug icon) after you're done with it, to avoid invoking Xdebug on every request and slowing down performance.
Command Line
As with the browser-based flow, this again requires you to first start a debugging session in VS Code and set a breakpoint on a line of code.
To trigger Xdebug when running command-line applications (such as when unit testing or running an Artisan command), you need to configure a specific environment variable.
As I'm working with Docker Compose, I would usually run unit tests like this:
The environment variable you need to set is XDEBUG_SESSION
and with the Xdebug configuration described above, it can be set to any value, as long as it's set. Xdebug's documentation suggests XDEBUG_SESSION=1
so we'll go with that:
That's it - when the code reaches a line where you've set a breakpoint, it should pause execution and focus that line within VS Code, where you can proceed to step through the code and track what's going on.
Conclusion
While this might seem like a long post compared to the actual amount of work that needs to be done to just configure Xdebug and VS Code and start debugging, I was attempting to describe my environment and setup in more detail so it can be more helpful to anyone running a similar setup - as well as trying to explain why all the required configuration is required in the first place, and what it does.
Hopefully this will work for you, but if it doesn't, feel free to let me know and I'll try to help out.
Stay healthy!
The command line debug client allows you to debug PHP scripts without having to set up an IDE.
Installation #
A binary for Linux, macOS, and Windows is available on the downloads page.You only have to download the binary, which you can then run from a command line.
Command Line Options #
The following command line options are available:
-1 | Debug once and then exit |
-f | Whether act as fully featured DBGp client. If this is enabled, the client will perform certain tasks for you, such as enabling async support. In the future, this mode will turn the dbgpClient in a fully fledged command line debugging client without the need for you to remember all DBGp commands. |
-h | Show this help |
-p value | Specify the port to listen on [9003 ]. This is the same port that Xdebug should initiate a connection on. On the Xdebug side, this port can be configured with the xdebug.remote_port setting. |
-r idekey | If the -r option is given, the client will register itself with a debugging proxy (selected with -y ), and then wait for incoming debugging connections as usual on the port configured with -p . |
-s | Enables SSL. With this option on, the client expects incoming connections on the configured port to be in SSL. This is an experimental feature that is not fully finished. Right now, Xdebug does not support SSL yet, but the dbgpProxy does. |
-u idekey | If the -u option is given, the client will unregister itself with a debugging proxy (selected with -y ), and then quit. |
-v | Show version number and exit |
-x | Show protocol XML. With this option enabled, the client will also show the raw XML data that the debugging engine sends back. This can be useful for debugging issues with the interaction between the debugger engine, for example Xdebug, and this client. |
-y host:port | Configures the host and port of an DBGp proxy. This option is used with -r and -u only. |
Usage #
To start the client on the command line on Linux, open a shell,and then run:
If the binary doesn't start or you get a not found
message,please refer to this FAQ entry.
To start the client on the command line on MacOS, open a shell,and then run:
On Windows, open a command a Command Prompt and run:
In all cases, you can add the command line optionsas described above.
When the debug client starts, it shows some version information. It willthen wait until a connection is made to it by a debugging engine (such asXdebug):
After a connection is received, the debug client shows basic information, and then shows the (cmd)
prompt and waits for input:
On this command prompt you can then enter the available DBGp commands. With{tab}
you can auto complete your command. You can use arrow up toa previous line, and use ctrl-R
to search through your previousissued commands.
Here we step into the next line (the first line, in our case), and see whichvariables are available:
The following commands and options are common:
Command | Description |
---|---|
breakpoint_set -t line -f file:///xdebug-test-2.php -n 5 | Sets a breakpoint on line 5 of file:///xdebug-test-2.php |
step_into | Steps to the next executable line in the code |
run | Runs the script until the next breakpoint, or when the script ends |
context_get | Lists the variables and their values in the current scope |
property_get -n $a | Retrieves the value of the property $a |
There is a full description in the DBGpdocumentation.
If the client has been started in 'fully featured mode' (-f
),and you're running the latest Xdebug from GitHub, then it is possible to pause a runningdebugging session by pressing Ctrl-C
. The client will return tothe prompt where you can then issue commands as normal.
On the client's prompt you can abort the debugging session, and then theclient, with Ctrl-C
.