Using Xdebug with IntelliJ inside a Docker container
By: Devin Byers
Tags: Xdebug, Docker, DevOps, Intellij, PHP

For a long time my development workflow consisted of editing code within Sublime Text on my local machine, pushing my changes to a remote development server, and debugging by using print_r() to write variables in places I suspected the bug was occurring. Rinse and repeat that process a thousand times and I’ve lost hours of productivity. My team made the decision to start using a new technology, Docker, and I figured it would be the perfect opportunity to start developing locally within containers while also taking advantage of IntelliJ and Xdebug.

Integrating all three of these technologies at the same time was pretty overwhelming and there wasn’t really anywhere online that brought all of them together so I decided to try my hand. This article will walk you through the steps of getting Xdebug on a PHP 7 container running Apache and using IntelliJ to set breakpoints within your code for runtime debugging.

First, I’m going to assume that you have at least a basic understanding of what Docker is as well as the IntelliJ ecosystem and obviously you’ll need both Docker and IntelliJ installed before getting started. I’m using Docker for Mac but I’m confident Docker Toolbox will work as well. I’ve included a link at the end of this article to a GitHub repository that can act as an example for playing along with minimal setup.

Setting up IntelliJ

I’m using IntelliJ instead of PhpStorm, so I had to install the PHP plugins before getting started. If you are using PhpStorm you should already have the required plug-ins and can skip this first step.

1. Install the PHP plugin

You can install the PHP plugin on IntelliJ by going to:

Settings > Plugins

Click on “Install JetBrains plugin” button and begin searching for PHP and installing it. You may have to restart IntelliJ after the installation is complete. You can see how your PHP plugins should appear after successfully installing below.

Screenshot 1: PHP plugin when it has been installed

2. Configure debugger settings

Once you’ve got the PHP plugin installed and have restarted IntelliJ you can configure remote debugging. Go to:

Preferences > Languages & Frameworks > PHP > Debug

From there you’ll want to set the Xdebug port to 9001 and make sure the “Can accept external connections” is checked (Screenshot 2). I prefer to also uncheck the “Force Break” options within the Xdebug section so that code breaks only happen where I set points. I also check the box to “Ignore external connections through unregistered server configurations” so that only servers I’ve set up through IntelliJ will be configured.

Screenshot 2: Debug preferences

After that, in the same settings window go to:

PHP > Debug > DBGp Proxy

From there you will set the host to 172.254.254.254 (I’ll go into why we set that later) and the port to 9001 as well.

Screenshot 3: IP and port set for DBGp Proxy

3. Set and configure server

Now you’ll want to set up the server configuration to map the files on your local machine to the remote host (which is technically just the Docker container running on your local machine). Click the “plus” symbol to create a new server configuration. Set the name to whatever you would like and the host to “localhost” you can set the port to anything as well; just note whatever you set it to for later because you’ll use it when you start up the container. Here I’m using 8880. The final step in setting up IntelliJ is to set the path mappings. Check the box “Use path mappings” and at the web directory on your local host set the absolute path to /app/web. That should be it for configuring your server.

Screenshot 4: Server configured with file mapping

4. Start listening for debug connections

Now that IntelliJ has been configured, all you have to do is tell it to start listening for external connections. Click on the little phone icon (shown below) in the top right of IntelliJ and you should be good to go.

Setting up Docker image

The next thing you will want to do is to create a development version of your Docker image. This is because we don’t really want Xdebug running on anything besides your local dev containers. You can do that by writing your own Dockerfile and building the image.

1. Write your Dockerfile

In this example I’m just using the official PHP 7 Docker Hub image found here: https://hub.docker.com/_/php/

We’ll use that image to create a new Dockerfile with Xdebug installed, enabled and configured. Below are the contents of the Dockerfile I have set for debugging

#use the official PHP7 image with apache

FROM php:7.0-apache



#install wget which is required to download xdebug

RUN apt-get update && apt-get install -y wget



# install xdebug and enable it. This block of code goes through the installion from source and compiling steps found

# on the xdebug website

# https://xdebug.org/docs/install

RUN cd /tmp \

&& wget https://xdebug.org/files/xdebug-2.5.0.tgz \

&& tar -zxvf xdebug-2.5.0.tgz \

&& cd xdebug-2.5.0 \

&& /usr/local/bin/phpize \

&& ./configure --enable-xdebug --with-php-config=/usr/local/bin/php-config \

&& make \

&& cp modules/xdebug.so /usr/local/lib/php/extensions/no-debug-non-zts-20151012/





# add xdebug configurations

RUN { \

echo '[xdebug]'; \

echo 'zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so'; \

echo 'xdebug.remote_enable=on'; \

echo 'xdebug.remote_autostart=on'; \

echo 'xdebug.remote_connect_back=off'; \

echo 'xdebug.remote_handler=dbgp'; \

echo 'xdebug.profiler_enable=off'; \

echo 'xdebug.profiler_output_dir="/var/www/html"'; \

echo 'xdebug.remote_port=9001'; \

echo 'xdebug.remote_host=172.254.254.254'; \

} > /usr/local/etc/php/conf.d/xdebug.ini



# add the application to the container outside of the web root

COPY app /app



# create a symlink to from the app to the web root

RUN mkdir -p /app && rm -fr /var/www/html && ln -s /app/web /var/www/html

This Dockerfile assumes that it is located at the root of your project and there is an “app” directory with a child directory named “web” (see below). This “web” directory will become the container’s web root.

2. Build the Image

Once you’ve created your Dockerfile you will need to build it by going into your terminal and running docker build :

⇒ docker build -t dbyers55/debugging-fun .

In case you’re not familiar with running the build command, the -t flag will name the image. Make sure to include the period at the end.

3. Running the image

Once the container is built you’ll have one more step before starting it. The host that was set during the IntelliJ configuration was set to 172.254.254.254. Even though Docker containers can be referenced as localhost, I haven’t been able to get the containers to reference the host machine in the same way. To get around this we will alias our local machine’s IP address to one referenced in the configurations. I do that with this command:

⇒ sudo ifconfig en0 alias 172.254.254.254 255.255.255.0

Keep in mind that whenever you restart your machine you will have to reset this IP alias.

Now you can finally start your Docker container. Run this command at the project root within your terminal:

⇒ docker run --rm -v `pwd`/app:/app -p 8880:80 --name debug-fun dbyers55/debugging-fun

The --rm option will completely remove the container after you stop it

option will completely remove the container after you stop it The -v flag will mount the volume from your machine to the container allowing your local edits to modify the container.

flag will mount the volume from your machine to the container allowing your local edits to modify the container. The -p flag maps port 80 inside the container to port 8880 on your local machine.

flag maps port 80 inside the container to port 8880 on your local machine. The --name option just makes it easier to reference the container

Finished!

You should be able to set some breakpoints in your code, and when you go to http://localhost:8880, IntelliJ should take focus and be highlighted on one of your breakpoints.

GitHub Link

Alternative Microsoft Visual Studio Code configuration

If you don’t have IntelliJ and want to use a free option, Microsoft Visual Studio Code is very easy to set up for the same debugging functionality. If you haven’t already installed VS Code, you will have to restart your machine for debugging to take effect. Remember to reset your IP alias if you do restart your machine.

Install the PHP Debug extension and reload VS Code On the debug tab, click the gear and select PHP A .vscode folder should have been created within your project with a launch.json file. Within the launch.json file, create a new configuration by adding this block right underneath “configurations” object.

{

"name": "Listen for XDebug port 9001",

"type": "php",

"request": "launch",

"port": 9001,

"localSourceRoot": "${workspaceRoot}/app/web",

"serverSourceRoot": "/app/web"

}

My final launch.json file looks like this:

{

"version": "0.2.0",

"configurations": [

{

"name": "Listen for XDebug",

"type": "php",

"request": "launch",

"port": 9000

},

{

"name": "Listen for XDebug port 9001",

"type": "php",

"request": "launch",

"port": 9001,

"localSourceRoot": "${workspaceRoot}/app/web",

"serverSourceRoot": "/app/web"

},

{

"name": "Launch currently open script",

"type": "php",

"request": "launch",

"program": "${file}",

"cwd": "${fileDirname}",

"port": 9000

}

]

}

Make sure to save the file once you’re done.

Set breakpoints where you wish and on the debug tab set the dropdown to “Listen for XDebug port 9001.” Hit the play button, load the page http://localhost:8880, and you should be able to use VS Code for debugging as well.