- Docker-compose, Dockerfile and XDebug
- Configuring PHPStorm
I decided to write this article because I was frustrated by how difficult it was to find all the information I needed to make these technologies work well together in a single article. I am hoping this resource can be a good reference for myself and for others who might run into the same issues that I ran into.
In this article we'll go through the following:
- Setting up the PhpStorm IDE so we can start and stop our docker-compose containers from within the IDE
- Debugging our code with PhpStorm and Xdebug (with Xdebug installed in a docker-compose service container)
- Running our test-suite with PhpStorm and PHPUnit (with PHP installed in a docker-compose service container)
Note: The information in this article should work well with PhpStorm version 2019.2 and up. Please be aware that some of the configuration options I am about to discuss are not available in certain older versions of PhpStorm.
The docker-compose.yml and Dockerfile configuration files I'm using for this article were created by Peter Fisher of How to Code Well and slightly modified by me. You can get access to the original files from his Freecodecamp PHP OOP course Github repository.
In the docker-compose file I'm using for this tutorial I have 3 services (2 database services and one web service) as you can see in this snippet:
version: '3' volumes: mysql_data: mysql_data_test: networks: dev_network: services: web: build: context: . ports: - 8080:80 volumes: - .:/var/www/html - ./dockerconfigs/000-default.conf:/etc/apache2/sites-enabled/000-default.conf - ./dockerconfigs/php.ini:/usr/local/etc/php/php.ini environment: APACHE_DOCUMENT_ROOT: /var/www/html/public XDEBUG_CONFIG: remote_host=host.docker.internal networks: - dev_network restart: on-failure db: image: mysql:5.7 volumes: - mysql_data:/var/lib/mysql environment: - MYSQL_DATABASE=devDB - MYSQL_ROOT_PASSWORD=test networks: - dev_network restart: on-failure db_test: image: mysql:5.7 volumes: - mysql_data_test:/var/lib/mysql environment: - MYSQL_DATABASE=devTestDB - MYSQL_ROOT_PASSWORD=test networks: - dev_network restart: on-failure
The web service from the docker-compose file above references this Dockerfile in its build context
FROM php:7.3-apache LABEL maintainer="Peter Fisher" RUN apt-get update \ && apt-get install -y \ git \ zlib1g-dev \ zip \ unzip \ libxml2-dev \ libgd-dev \ libpng-dev \ libfreetype6-dev \ libjpeg62-turbo-dev \ libzip-dev \ && pecl install xdebug \ && docker-php-ext-install mysqli pdo_mysql iconv simplexml \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ && docker-php-ext-configure zip --with-libzip \ && docker-php-ext-install gd zip \ && docker-php-ext-enable xdebug \ && apt-get clean all \ && rm -rvf /var/lib/apt/lists/* \ && a2enmod rewrite headers RUN curl -sS https://getcomposer.org/installer | php -- --filename=composer --install-dir=/bin ENV PATH /root/.composer/vendor/bin:$PATH EXPOSE 9000 WORKDIR /var/www/html
Note: Xdebug, PHP, PHP-CLI and Apache are installed in the Dockerfile for the web service. Also notice that Xdebug's port 9000 is exposed.
In the web service for the docker-compose file I am binding a php.ini file from my host machine into the web service container.
services: web: build: context: . ports: - 8080:80 volumes: - .:/var/www/html - ./dockerconfigs/000-default.conf:/etc/apache2/sites-enabled/000-default.conf - ./dockerconfigs/php.ini:/usr/local/etc/php/php.ini
The php.ini file I am binding to the container has some configuration options for Xdebug added at the end like so:
[XDebug] xdebug.remote_port=9000 xdebug.remote_enable=1 xdebug.remote_connect_back=1 xdebug.remote_autostart=1
There are other ways to configure xdebug besides modifying your php.ini file. Specifically you could use an xdebug configuration file or you could add the configurations for xdebug as an environment variable right in your docker-compose. In the docker-compose file I'm using for this article, I set the xdebug configuration for remote_host under the web service container like so
environment: APACHE_DOCUMENT_ROOT: /var/www/html/public XDEBUG_CONFIG: remote_host=host.docker.internal
So putting everything together my PHP project in PhpStorm currently looks like this (it's a new Laravel project I scaffolded for this tutorial).
If you don't see this create option, wait a bit and right click again. I observed that it takes a while to show up.
A configuration box will pop up where you can modify some options. I usually change the name of my configuration to something more memorable and I like to check the force builds option. But you can leave the defaults.
Configure PHPStorm's local server to point to your docker service by going to File > Settings > Languages & Frameworks > PHP > Servers. Add a new server and set the host to localhost and the port to whatever port you are publishing your docker-compose web service to.
In my case I am publishing my web service's port 80 to my host container's port 8080 so I'd be filling in 8080 for port.
Ensure to select the Connect to existing container option. This is important if you're working with a separate container for your database and it ensures that PhpStorm has access to the network your containers are joined to. If you select the Always start a new container option, then each time your run PHPUnit within PhpStorm, a new container that is not joined to any of your networks will be created.
Go to File > Settings > Languages & Frameworks > PHP > Test Frameworks and add a new PHPUnit by Remote Interpreter configuration. In the dialog that comes up, select the CLI interpreter we created in the previous step from the drop-down.
Now you should be able to run your PHPUnit tests within PhpStorm without doing a docker exec into your service container.
Go to File > Settings > Languages & Frameworks > PHP > Debug and ensure PhpStorm is listening for Xdebug on port 9000 and can accept external connections. Then ensure that PhpStorm is listening for debug connections.
- If you find that you're not able to receive Xdebug connections then ensure your path mappings are in order see this step
- If you are using multiple containers in a network, ensure that your PHP CLI interpreter has the Connect to existing container option set