Setup xDebug with Docker. PHPStorm & VSCode configuration

Table of Content

Introduction

After the several bunch setup of Docker+xDebug+PHPStorm I understood that there are some moments which should be noted. I’m going to explain in details how to setup Docker that it’ll works correctly on Linux, macOS and Windows.

Port availability

xDebug will need a port (usually its port 9000). To make sure the port that we will use for xDebug is available, lets check which ports are in use:

nc -w5 -z -v 127.0.0.1 9000
Connection to 127.0.0.1 9000 port [tcp/*] succeeded!

Despite the fact running on local machine Docker is virtual instance. We should consider it as dedicated machine which communicate with our machine through IP or port. To open port for external connection run text command as superuser:

sudo iptables -I INPUT -p tcp --dport 9000 -j ACCEPT

NOTE: You always can remove the rule just replace -I with -D. Take a look at this rule sudo iptables -D INPUT -p tcp --dport 9000 -j ACCEPT

Docker Setup

Dockerfile is prety simple, we use php:7.2-fpm image and add xDebug configuration:

FROM php:7.2-fpm

# Set server timezone
RUN rm /etc/localtime
RUN ln -s /usr/share/zoneinfo/Europe/Kiev /etc/localtime

# Set PHP timezone
RUN printf '[PHP]\ndate.timezone = "Europe/Kiev"\n' > /usr/local/etc/php/conf.d/tzone.ini

# Install and Setup xDebug
RUN apt-get update \
# Add "ip" tools for resolve "host.docker.internal" through entrypoint automatically, because there is no suport "for-linux" at this time (@see https://github.com/docker/for-linux/issues/264)
    && apt-get -y install iproute2 \
    && apt-get -y install iputils-ping \
    && pecl install xdebug-2.6.0 \
    && docker-php-ext-enable xdebug \
    && touch /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "error_reporting=E_ALL" > /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "display_startup_errors=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "display_errors=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    # Full path to xdebug.so -> =/usr/local/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so
    && echo "zend_extension=xdebug.so" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_enable=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_autostart=0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_connect_back=0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_handler=dbgp" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.idekey=PHPSTORM" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_port=9000" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.profiler_enable=0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.profiler_output_dir=\"/var/www/xdebug\"" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

Docker-compose setup

docker-compose.yml has some interesting options which I describe in detail below.

version: '3'

services:
  app:
    build:
      context: .
      dockerfile: .docker/php/Dockerfile
    environment:
      PHP_IDE_CONFIG: "serverName=app" # PHPStorm Server name
      XDEBUG_CONFIG: "remote_host=host.docker.internal"
    working_dir: /var/www/html
    entrypoint: ["./.docker/php/init.sh"]
    volumes:
      - .:/var/www/html
    ...

Network setup

There is network_mode: "host" feature which can resolve problem with unified host but it works only on Linux, so this approach is rejected.

The next two configurations are only actual if you are on Linux machine otherwise you can skip this step.

Variant A

Docker on Windows and macOS setup host.docker.internal host under the hood and this allow connect from Docker container to Docker host with unified name, but this feature in not available on Linux yet.

The issue with this problem is registered on GitHub and even there is PR which add support for host.docker.internal on Linux. When this PR will be accepted we can use host.docker.internal without any hacks on Linux.

We specialy add entrypoint to docker-compose config, this script allow us resolve host.docker.internal if we on Linux machine. So, add next script to .docker/php/init.sh script:

#!/usr/bin/env bash

HOST_DOMAIN="host.docker.internal"
ping -q -c1 HOST_DOMAIN > /dev/null 2>&1;
if [[ $? -ne 0 ]]; then
    HOST_IP=$(ip route | awk 'NR==1 {print $3}');
    echo -e "$HOST_IP\t$HOST_DOMAIN" | tee -a /etc/hosts;
fi

php-fpm -F

This script check if host.docker.internal can be resolved and on the failure add record to /etc/hosts file.

Variant B

You can add qoomon/docker-host image to your docker-compose.yml then change xdebug.remote_host to xdebug.remote_host=dockerhost in all places and it will works.

version: '3'

services:
  app:
    build:
      context: .
      dockerfile: .docker/php/Dockerfile
    environment:
      PHP_IDE_CONFIG: "serverName=app" # PHPStorm Server name
      XDEBUG_CONFIG: "remote_host=host.docker.internal"
    working_dir: /var/www/html
    entrypoint: ["./.docker/php/init.sh"]
    volumes:
      - .:/var/www/html
    depends_on: [ dockerhost ]
  ...
  dockerhost:
    image: qoomon/docker-host
    cap_add: [ 'NET_ADMIN', 'NET_RAW' ]
    restart: on-failure

depends_on: [ dockerhost ] is required it this case.

IDE configuration

PHPStorm

  1. Go to the Settings (Ctrl+Alt+S) Languages & Frameworks > PHP > Debug > xDebug (screenshot)

    • Debug port – 9000

      Debug port must be equal to xdebug.remote_port=9000 in .docker/php/Dockerfile file

  2. Go to the Settings (Ctrl+Alt+S) Languages & Frameworks > PHP > Servers

  3. Press green "+" button, enter next parameters (screenshot), all options are important:

    • Name – app

      Name must be equal to PHP_IDE_CONFIG: "serverName=app" environment variable in docker-compose.yml, but you can use any name, app is only as example.

    • Host – localhost
    • Port – 8080
    • Debugger – Xdebug
    • Check Use path mappings…: [path/to/project/root-dir] -> /var/www/html.

      Hint: To submit the "absolute path on the server" press Enter after typing the path in the text field. If you only click out of the field, your input will be removed.

  4. Press "Apply" and "OK";

  5. Reload Docker with:

    $ ./bin/docker-compose down
    $ ./bin/docker-compose up
  6. Set breakpoint in your code

  7. Press Start listening for PHP Debug Connection in PHPStorm

  8. Open project in Web browser on http://localhost:8080.

VSCode

Before integrate VSCode with xDebug I recommend you install the PHP Debug and PHP IntelliSense extensions from Felix Becker and PHP Intelephense extension from Ben Mewburn. Strictly speaking, you can debug without the PHP IntelliSense and PHP Intelephense extensions, but it’s very nice to have.

For setup xDebug click Debug icon on the left panel and then click Open launch.json
file

Past next configuration to it

// launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "port": 9000,
            "log": true,
            "externalConsole": false,
            "pathMappings": {
                "/app": "${workspaceFolder}"
            },
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9000
        }
    ]
}

You need to figure out the right pathMappings for your setup as not all the PHP containers you find in the docker registry specify /app as the root path. Just check out what your webroot’s path is and map that to ${workspaceFolder} assuming that you launched VSCode from your applications root.

NOTE: in pathMappings, the left part is path in Docker container, and the right part is the path on your local machine.

Next you can start debugging your application by defining a breakpoint at a section you want to take a closer look at and starting your previously defined Listen for XDebug script by clicking the small play button on the top left corner in VSCode.
You can define breakpoints by clicking right next to the line numbers in an open .php file as a red dot appears.

Open project in Web browser on http://localhost:8080.

Useful links

Leave a Reply

Your email address will not be published. Required fields are marked *