DEV Community

Mert Simsek
Mert Simsek

Posted on

Using Xdebug with Docker

I am going to show using Xdebug with Docker. Because, as long as you prefer using Xdebug on development workspace with Docker, you have to create remote connection of the Xdebug. First of all, you must have a Xdebug client. I would rather use PhpStorm for this. With this way, I am going to mention some hints and ways to use smoothly. Let's start!

I would like to start a Symfony project which is running on Docker. I will tell this independent from Symfony. Through Docker Compose helps me about it. My structure is like as the following.

alt text

Well, I share my Docker assets. Here you are. This is php.ini file.

[PHP]
engine = On
short_open_tag = On
precision = 14
output_buffering = Off
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = 100
disable_functions = dl
disable_classes =
zend.enable_gc = On
expose_php = Off
max_execution_time = 30
max_input_time = 60
memory_limit = 256M
error_reporting = E_ALL & ~E_NOTICE
display_errors = On
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
html_errors = On
error_log = error_l
variables_order = "EGPCS"
request_order = "GP"
register_argc_argv = On
auto_globals_jit = On
post_max_size = 64M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
include_path = ".:/opt/php72/lib/php"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 64M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = On
default_socket_timeout 
cli_server.color 
date.timezone = Asia/Riyadh
pdo_mysql.cache_size = 2000
pdo_mysql.default_soc
sendmail_path = /usr/sbin/sendmail -t 
mail.add_x_header 
sql.safe_mode =
odbc.allow_persistent = Off
odbc.check_persistent = Off
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode
ibase.allow_persistent = 1
ibase.max_persistent = -1
ibase.max_links = -1
ibase.timestampformat = "%Y-%m-%d %H:%M:%S"
ibase.dateformat = "%Y-%m-%d"
ibase.timeformat = "%H:%M"
mysqli.max_persistent = -1
mysqli.allow_persistent = Off
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect =
mysqlnd.collect_statistics = Off
mysqlnd.collect_memory_statistics =
pgsql.allow_persistent = Off
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice
bcmath.scale
session.save_handler = files
session.save_path = "/tmp"
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.hash_function = 0
session.hash_bits_per_character = 5
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=,fields"
zend.assertions 
tidy.clean_output =
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit
ldap.max_links

Second one is nginx.conf.

server {
    listen 80;
    server_name web;
    root /var/www/app/public;

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        #fastcgi_pass unix:/var/run/php7.2-fpm.sock;
        fastcgi_pass php:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        internal;
    }

    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

And third one is the Dockerfile. The important points are on here.

FROM php:7.2-fpm-alpine

RUN apk update \
    && apk add  --no-cache git mysql-client curl libmcrypt libmcrypt-dev openssh-client icu-dev \
    libxml2-dev freetype-dev libpng-dev libjpeg-turbo-dev g++ make autoconf \
    && docker-php-source extract \
    && pecl install xdebug redis \
    && docker-php-ext-enable xdebug redis \
    && docker-php-source delete \
    && docker-php-ext-install pdo_mysql soap intl zip \
    && echo "xdebug.remote_enable=on" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_autostart=off" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_port=9001" >> /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.remote_connect_back=0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.idekey=mertblog.net" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.remote_host=docker.for.mac.localhost" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
    && rm -rf /tmp/*

CMD ["php-fpm", "-F"]

WORKDIR /var/www/app

EXPOSE 9000

If you haven't got Mac OS, you're supposed to change xdebug.remote_host as your machine IP. In addition to this, as you see, my idekey is mertblog.net and my remote_port is 9001. You are able to change it. Finally, let me show docker-compose.yml file.

version: '3'
services:
    php:
        build:
            context: ./php-fpm/.
        volumes:
            - ./app:/var/www/app
            - ./php-fpm/php.ini:/usr/local/etc/php/php.ini
        depends_on:
            - mysql
    web:
        image: nginx:latest
        ports:
            - "8888:80"
        volumes:
            - ./app:/var/www/app
            - ./nginx/app.conf:/etc/nginx/conf.d/default.conf
        depends_on:
            - php
    mysql:
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: symf0ny
        ports:
            - "3333:3306"

I installed the Symfony application which I've refered this document. I started my web application by using this command.

docker-compose up --build

alt text

From now on, I could configure the Xdebug client. For this, follow this path from the interface. Phpstorm > Preferences > PHP > Debug. You are going to see Xdebug Debug Port. You should change as 9001 port. After that, follow this path Phpstorm > Preferences > PHP > Servers. You are going to see this screen.

alt text

I've set the name docker-server. My host is localhost and my web server post is 8888. Click the Use path mappings. You have to define the Absolute path on the server as /var/www/app. It links to our project directory on the local machine. Well, after this, you close this screen and click the Edit Configurations section on the left side.

alt text

alt text

On this section, we are going to create a configuration under to PHP Remote Debug. I've set the name as docker. My server is docker-server which I've created just before. And Ide Key is same thing in the Dockerfile.

I clicked the phone icon to start listening PHP Debug Connections.

alt text

Right, I can start my Xdebug session by getting this url. http://localhost:8888/home?XDEBUG_SESSION_START=mertblog.net

I've put red point on the 19th line. From now on, I am able to debug my variables on PHPStorm interface obviously. It truly helps us and with this way we could notice mistakes easily.

alt text

Top comments (11)

Collapse
 
sergeytelpuk profile image
Sergey

I see that you put xdebug config direct in dockerfile it's bad approach. You can create a file
called xdebug.ini it can look like the following:

[Xdebug]
xdebug.remote_autostart=0
xdebug.remote_enable=1
xdebug.default_enable=0
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9000
xdebug.remote_connect_back=0
xdebug.profiler_enable=0
xdebug.remote_log="/tmp/xdebug.log"
xdebug.profiler_output_dir="/var/www/html/profiler"
xdebug.profiler_output_name="cachegrind.out.%p"
xdebug.cli_color=1
xdebug.profiler_append=1

after that copy that one into /usr/local/etc/php/conf.d/

Collapse
 
joppedc profile image
JoppeDC

This is a lot cleaner, also since we use prebuilt docker images we cant just change the dockerfile. I've not been able to get it to work the way you explained, but i'll try again later ;)

Collapse
 
_mertsimsek profile image
Mert Simsek

Thank you for reply, it's prettier solution as you said.

Collapse
 
dvdvdmt profile image
Dmitriy Davydov • Edited

I've used this xdebug.ini placed in /usr/local/etc/php/conf.d/

[Xdebug]
xdebug.remote_enable = true
xdebug.remote_port = 9000
xdebug.remote_host = host.docker.internal

Where host.docker.internal is the proper way to access your host ip address in a containerized app in Docker Desktop clients.

NOTE: Set your debug port in WebStorm to 9000 (Languages & Frameworks > PHP > Debug).

Also you need to add the next line to your Dockerfile or set environment field in docker-compose.yml

ENV PHP_IDE_CONFIG="serverName=<server-name-in-PhpStorm-servers>"

After that you need to press "Start listen on PHP debug connections" (phone icon), set a breakpoint and start a debug session (bug icon). To trigger the debug session you access your API endpoint via GET, POST, with query param ?XDEBUG_SESSION_START=PHPSTORM or cookie set to XDEBUG_SESSION = PHPSTORM. To set this cookie easily you can use Xdebug Helper extension or a similar one.

Collapse
 
adamsandor profile image
Ádám Sándor

Thank you so much for this guide! I was struggling all day to make my Wordpress in Docker on Windows setup work with xdebug. This post had all the missing details like how to have xdebug connect to PhpStorm running on the host.

Collapse
 
_mertsimsek profile image
Mert Simsek

I'm glad to hear that, happy coding :)

Collapse
 
garronarg profile image
Garron Argentina

Why you use the big php.ini like this. Something looks unnecessary.

Collapse
 
_mertsimsek profile image
Mert Simsek

yep, I just wanted the audience to know we can do.

Collapse
 
mohsinsaeed profile image
mohsin-saeed

Hello Dear! In my case ,following your last point, browser keeps loading. But debbuger variables area is not being populated. Can you please help

Collapse
 
_mertsimsek profile image
Mert Simsek

Hi! Is there any error? How can I help you with it?

Collapse
 
mohsinsaeed profile image
mohsin-saeed • Edited

There is no error. But variables are not also being shown in debugger area.

I am not using Symphony, but a simply all code is in index.php file within app/public folder as specified by you. Can there be any mapping issue in this case? My mappings are same as specified by you