DEV Community 👩‍💻👨‍💻

Jack Miras
Jack Miras

Posted on • Updated on

Xdebug in VSCode with Docker

In my last post, I've talked about how to configure a development environment and how it extends a Dockerfile made for production.

Now, I would like to share how we can build upon our previous Dockerfile in a way that Xdebug can run directly from Docker and also connect it with Visual Studio Code.

By choosing this approach, we substantially reduce the amount of setup that each team member has to do on their machine to get the project up and running, which means that we can start writing code faster.

So, why is this so important? A recent research from JetBrains shows that 68% of the PHP developers debug their code using var_dump(), die(), dd() and dump(). From my perspective, there is nothing wrong with that. Even if you do it by choice and not because you lack knowledge.

I'm included in the 68% of developers debugging their code with auxiliary functions instead of using a full-featured debug solution such as Xdebug. Not for lack of knowledge but because I'm a heavy Neovim user and I didn't adapt quite well using Neovim with Xdebug, to me, is just easier and faster to use my code snippets around the dd() function.

But from time to time, I caught myself in situations where it would be faster to jump into Visual Studio Code and just use Xdebug, especially when I'm working with other people that aren't familiarized with Vim/Neovim.

Content

Xdebug config file

Before jumping into PhpStorm, first we have to clear a few things about Xdebug to fully grasp the changes we’re going to make on the IDE. The information got first introduced on the topic about the command directive in a previous post. You will notice that at some point a xdebug.ini file gets copied from a local .docker folder into /etc/php8/conf.d/50_xdebug.ini at the container.

Even though the content of the file got shown, I intentionally didn't explain its content so that we could explore the debugging topic all at once, going all the way from configuring Xdebug to using it with an IDE.

Down below, we have the same Xdebug config file, from the previous post, placed at .docker/xdebug.ini on the root of our Laravel project. Each line of code will be explained further, but in case you want to know every configuration that you can add in this file, check the Xdebug documentation.

zend_extension=xdebug.so
xdebug.mode=develop,coverage,debug,profile
xdebug.idekey=docker
xdebug.start_with_request=yes
xdebug.log=/dev/stdout
xdebug.log_level=0
xdebug.client_port=9003
xdebug.client_host=<YOUR_COMPUTER_IP>
Enter fullscreen mode Exit fullscreen mode
Explaining xdebug.ini
  • zend_extension=xdebug.so

A Zend extension hooks into “lower level” languages, a single extension can be both a PHP and a Zend extension, despite being very uncommon it's possible and Xdebug is a good example of it.

  • xdebug.mode=develop,coverage,debug,profile

This setting controls which Xdebug features got enabled, according to the documentation the following values gets accepted:

- develop
    Enables Development Helpers, including the overloaded var_dump().
- coverage
    Enables Code Coverage Analysis to generate code coverage reports, mainly with PHPUnit.
- debug
    Enables Step Debugging. This can be used to step through your code while it is running, and analyze values of variables.
- profile
    Enables Profiling, with which you can analyze performance bottlenecks with tools like CacheGrind.
Enter fullscreen mode Exit fullscreen mode
  • xdebug.idekey=docker

Controls which IDE Key Xdebug should pass on to the debugging client or proxy. The IDE Key is only important for use with the DBGp Proxy Tool, although some IDEs are incorrectly picky as to what its value is. The default is based on the DBGP_IDEKEY environment setting. If it is not present, the default falls back to an empty string.

  • xdebug.start_with_request=yes

The functionality starts when the PHP request starts, and before any PHP code getting executed. For example, xdebug.mode=trace and xdebug.start_with_request=yes starts a Function Trace for the whole request.

  • xdebug.log=/dev/stdout

Configure Xdebug's log file, but in here, we are redirecting the log content to the default stdout of our container.

In case you don't want to see these logs you can comment out this line of your .docker/xdebug.ini file by changing the line to ;xdebug.log=/dev/stdout.

  • xdebug.log_level=0

Configures which logging messages should be added to the log file. In here we are instructing Xdebug to log only errors in the configuration, in case you want to see more information you can use the level 7 for log info or the level 10 for log debug.

  • xdebug.client_port=9003

The port to which Xdebug tries to connect on the remote host. Port 9003 is the default for both Xdebug and the Command Line Debug Client. As many clients use this port number, it is best to leave this setting unchanged.

  • xdebug.client_host=<YOUR_COMPUTER_IP>

Configures the IP address or hostname where Xdebug will attempt to connect to when initiating a debugging connection. This address should be the address of the machine where your IDE or debugging client is listening for incoming debugging connections.

Down below, you can see how to correctly get your IP address in the main OS developers use. In case you are using a different OS, the commands may serve you as base to try to extrapolate a solution for your use case.

macOS:

ipconfig getifaddr en0
Enter fullscreen mode Exit fullscreen mode

Windows with WSL:

grep nameserver /etc/resolv.conf | cut -d ' ' -f2
Enter fullscreen mode Exit fullscreen mode

Linux (Debian based distros):

hostname -I | cut -d ' ' -f1
Enter fullscreen mode Exit fullscreen mode

Once you correctly found your IP address, you can place him into the xdebug.client_host as mentioned before, and that will leave you with a directive looking similar to this xdebug.client_host=192.168.0.158.

In summary, you've instructed Xdebug to start from a request and try to send the debug events to the host with the IP 192.168.0.158 on the port 9003. Since the IP represents your computer, these means that when configuring Visual Studio Code to connect into Xdebug, the configuration will be extremely similar to when connecting to the localhost.

Visual Studio Code

As you may already know, Visual Studio Code or, VSCode for short, is a source-code editor made by Microsoft for Windows, Linux, and macOS. Features include support for debugging, syntax highlighting, intelligent code completion, snippets, code refactoring, and embedded Git.

With that being said, you may be wondering… what do we need to have VSCode with all the aspects of an IDE with full-featured debug?

For starters, we will need to install the PHP Debug plugin from Felix Becker:

install-php-debug-plugin

Thereafter, a file called launch.json have to be generated, this file gets used by the debugger in any language, which means that a part of this process can be reused when configuring the debugger on VSCode for other languages.

You can generate the file by clicking in Run and Debug > create a launch.json file > Docker: Debug in Container, as the following screenshot shows:

setting-up-debug

Finally, the launch.json file will be created with a specification of version and an empty configurations array, just like the next screenshot shows:

debug-launch-json-without-configurations

Now, to properly configure the launch.json file you have to add in the configurations array an object with the properties name, type, request, port, and pathMappings which will leave you with a file looking like this:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug on Docker",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "pathMappings": {
                "/var/www/html/": "${workspaceFolder}"
            }
        }
    ]
}

Enter fullscreen mode Exit fullscreen mode

Explaining launch.json

  • name

Indicates the name given to a configuration object.

  • type

Indicates the underlying debugger getting used.

  • request

Indicates whether the configuration is intended to launch the program or attach to an already running instance.

  • port

Indicates the port on which to listen for Xdebug

  • pathMappings

Indicates a mapping of server paths to local paths.

When using /var/www/html/ as key, VSCode knows that the files at the container are under that path, and by using the ${workspaceFolder} as value, VSCode knows that locally the project files are under the current opened directory.


I encourage you to leave this launch.json file with its content in the project, so other team members can just clone the project, run the containers and enjoy a full-featured debug solution running in a container environment.

Happy coding!

Top comments (10)

Collapse
 
apmyp1990 profile image
apmyp1990

Hi, thanks for the guide. I am using VSCode from WSL2 with Docker installed in WSL2. But this setup will not work for me.
Do you have any suggestsions?

Thanks in advance

Collapse
 
jackmiras profile image
Jack Miras

Hi @apmyp1990, what problems are you facing?

Do you have any logs that you can share?

Collapse
 
apmyp1990 profile image
apmyp1990

I solved the problem - this line was missing:

xdebug.discover_client_host=true
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
luksladky profile image
Lukáš Sladký

Kudos for posting the answer to your problem after you'd solved it.

Thread Thread
 
apmyp1990 profile image
apmyp1990

A few days I ago I had to sovle the same problem again, because I hat to re-setup my local develpment. I was really happy to find my old setup here :-D

Thread Thread
 
tkorakas profile image
Thanos Korakas • Edited on

On Windows with WSL2 and Docker I had to add the "hostname" property to the launch.json with the value "0.0.0.0".
"hostname": "0.0.0.0"
Also, I didn't have to specify my IP address on the xdebug.ini host field. I used the host.docker.internal, xdebug.client_host='host.docker.internal' and xdebug.discover_client_host=0

Thanks a lot for your great article

Thread Thread
 
jackmiras profile image
Jack Miras

@tkorakas how are you doing?

Thanks for adding the notes, I really appreciate it!

I just would like to leave it an observation about setting xdebug.client_host=host.docker.internal... Last time I've checked this didn't work for all OS (Linux, Mac, and Windows) while setting the IP works for the three of them.

For anyone else reading this comment, if your OS works by setting the client host this way, I recommend that you do it because it will save you from having to update your IP address from time to time.

Also, you probably had to set "hostname": "0.0.0.0" because of the change at the xdebug.client_host but it's not a big change.

Once again, thanks for adding the notes.

Thread Thread
 
tkorakas profile image
Thanos Korakas

Your comments makes absolute sense and it might save a lot of time from people reading this article.

Linux users where host.docker.internal is not working could try to add this lines on their docker compose file

extra_hosts:
- "host.docker.internal:host-gateway"
Enter fullscreen mode Exit fullscreen mode

On the fpm container. Note: I didn't have the chance to test it

The main reason I used host.docker.internal is to be able to use this one different machines without having to change anything, or sharing with others.

Again, thanks a lot for your article. It was one the best written that I found on this topic

Collapse
 
apmyp1990 profile image
apmyp1990

I tried to tell my xdebug (in docker) the ip of my Windows Host hardcoded and via host copied from wsl to docker (that worked for another case), but the breakpoints were never hit.
Unfortunately I do not have any logs.

Here's my setup:


xdebug.ini

zend_extension=xdebug.so

xdebug.mode=debug,develop
xdebug.client_host=172.28.32.1
#same port as in launch.json
xdebug.client_port=9003
xdebug.idekey=VSCODE
xdebug.start_with_request=yes

launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug on Docker",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "pathMappings": {
                "/amadeus360": "${workspaceFolder}"
            },
        },
    ]
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
snakepy profile image
Fabio

Hey really appreciated this guide, it helped me a lot to create my favorite dev env for laravel. I wrote about it here dev.to/snakepy/my-favorite-laravel...

I'd appreciate any improvement suggestions.

Stop sifting through your feed.

Find the content you want to see.

Change your feed algorithm by adjusting your experience level and give weights to the tags you follow.