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 rulesudo 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
-
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
- Debug port – 9000
-
Go to the Settings (Ctrl+Alt+S) Languages & Frameworks > PHP > Servers
-
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 indocker-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.
- Name – app
-
Press "Apply" and "OK";
-
Reload Docker with:
$ ./bin/docker-compose down $ ./bin/docker-compose up
-
Set breakpoint in your code
-
Press
Start listening for PHP Debug Connection
in PHPStorm -
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
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.