PhpStorm + Docker + Xdebug

В этой небольшой статье я опишу рабочее решение настройки Xdebug для использования его при отладке в PhpStorm с использованием удаленного интерпретатора PHP, работающего внутри Docker-контейнера.

В итоге получится конфигурация на базе окружения в Docker-контейнерах, которая позволит производить отладку Web-приложения, консольного приложения, тестов, запускаемых из консоли и запускаемых из PhpStorm.

Исходные данные

У вас должны быть установлены Linux, PhpStorm, Docker, Docker-compose. У меня в блоге есть небольшая памятка, как установить Docker и docker-compose в Linux. Если Вы ведете разработку не на Linux, то Вам, наверное, придется чуть сложнее, но раз Вы читаете эту статью, значит Вы уже озадачились вопросом отладки с использованием php в контейнере Docker и у Вас уже всё давно установлено. Если настройки для MacOS или Windows будут отличаться от приведенных в статье, я обязательно укажу на это.

Статья будет рассмотрена на следующем примере. Каталог на локальной машине (хосте), в котором планируется разработать некоторое приложение: /home/denis/code/docker-xdebug. При этом корень Web-сервера находится в подкаталоге public. Основной каталог проекта отображается внутрь Docker-контейнеров на каталог /var/www. Внутри Docker-контейнера, при этом, получаем путь к корневому каталогу Web-сервера /var/www/public — именно на него настроен Web-сервер nginx.

Пример конфигурации

На самом деле, поставленная задача достаточно тривиальная. Все решения, которые мне удавалось находить ранее, были завязаны на IP-адресе хоста и их невозможно было использовать остальными членами команды, так как в каждом новом случае IP-адрес хоста был случайный и каждому члену команды приходилось бы менять IP-адрес на свой в общем файле конфигурации бандла, что крайне неудобно.

Решение проблемы оказалось достаточно простым. Я просто создал общую сеть для бандла в docker-compose (192.168.220.0/28) и таким образом добился одинакового IP-адреса хоста (192.168.220.1) на всех машинах членов команды разработки.

Если Вы используете MacOS или Windows, то вместо адреса 192.168.220.1 Вам нужно будет указать host.docker.internal в приведенном ниже docker-compose.yml.

Ниже приведу подробную инструкцию и все файлы, необходимые для сборки бандла с рабочей отладкой Xdebug.

В файлах отсутствует установка каких либо дополнительных зависимостей и расширений, так как в каждом проекте они свои. Это выходит за рамки данной статьи. Вы должны сами добавить их при необходимости.

docker-compose.yml

version: '3'
services:
  php-fpm:
    build:
      context: docker/php-fpm
    volumes:
      - ./:/var/www
    environment:
      XDEBUG_CONFIG: "remote_host=192.168.220.1 remote_enable=1"
      PHP_IDE_CONFIG: "serverName=Docker"
    networks:
      - internal
  nginx:
    build:
      context: docker/nginx
    volumes:
      - ./:/var/www
    ports:
      - "80:80"
    depends_on:
      - php-fpm
    networks:
      - internal
networks:
  internal:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.220.0/28

Если у вас MacOS или Windows, то вместо remote_host=192.168.220.1 используйте remote_host=host.docker.internal.

docker/php-fpm/Dockerfile

FROM php:7.2-fpm

RUN apt-get update && apt-get install -y wget git unzip \
    && pecl install xdebug-2.7.1 \
    && docker-php-ext-enable xdebug

ADD ./php.ini /usr/local/etc/php/php.ini

RUN wget https://getcomposer.org/installer -O - -q \
    | php -- --install-dir=/bin --filename=composer --quiet

WORKDIR /var/www

docker/php-fpm/php.ini

max_execution_time = 1000
max_input_time = 1000

Это необходимо для того, чтобы Вы успели изучить результаты отладки до истечения таймаута запроса.

docker/nginx/Dockerfile

FROM nginx

ADD ./default.conf /etc/nginx/conf.d/default.conf

WORKDIR /var/www

docker/nginx/default.conf

server {
    listen 80;
    index index.php;
    server_name 127.0.0.1 localhost;
    root /var/www/public;

    location / {
        try_files $uri /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        fastcgi_read_timeout 1000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

Теперь просто запускаем бандл docker-контейнеров:

$ docker-compose up -d --build

Настройка PhpStorm

Настройка отладки в PhpStorm типичная для подобных ситуаций. Необходимо в настройках (File — Settings) проделать следующее:

Добавить Docker сервер

Если он у Вас еще не добавлен, конечно. Для этого откройте настройки: «Build, Execution, Deployment» — Docker. Затем нажмите плюсик, чтобы добавить новое подключение к докеру. У меня все подключилось сразу же в режиме Unix socket. Нажмите Apply.

Добавление нового Docker сервера

Добавить внешний интерпретатор

Откройте настройки: «Languages & Frameworks» — PHP

Справа от CLI Interpreter нажмите кнопку с тремя точками.

Выбор версии PHP и добавление интерпретатора

В открывшемся окне CLI Interpreters нажмите плюс слева вверху и выберите там From Docker….

Далее нужно выбрать Docker (либо в новых версиях PhpStorm можно также выбрать Docker Compose). Второй вариант мне показался удобней, так как в этом случае я вижу контейнеры только данного проекта, а не все существующие на хосте (у меня их довольно много).

Два варианта на выбор:

Docker
Docker Compose

Не забывайте правильно указывать наименование образа или сервиса. Нажмите ОК, после чего будет произведена попытка добавления выбранного интерпретатора из Docker.

В случае удачи, вы увидите следующее окно (здесь видно, что была определена версия PHP, конфигурационный файл PHP, а также, что очень важно, версия Xdebug. Переименуйте интерпретатор в нечто более понятное, например, как на скриншоте ниже.

Начиная с версии PhpStorm 2019.1 появилась возможность выбора, каким образом запускать тесты в контейнере. Если у вас контейнер с постоянно работающей службой, такой как php-fpm, то выбирайте Connect to existent container. Если же у вас в качестве интерпретатора используется php-cli, который не выполняется постоянно, то выберите опцию Always start a new container. Нажмите OK.

Настройка интерпретатора PHP

Теперь Вы должны увидеть следующее:

Path mappings берется из docker-compose.yml

Нажмите Apply.

Проверить конфигурацию Xdebug

Откройте настройки: «Languages & Frameworks» — PHP — Debug. И нажмите на ссылку Validate.

Нажмите Validate для проверки настройки

Откроется окно, в котором Вы должны указать полный путь к web-корню (в нашем примере это подкаталог проекта public) и адрес сервера (в нашем примере порт 80 контейнера nginx транслируется в порт 80 хоста, поэтому в адресе порт не указывается. Если Вы транслировали в другой порт хоста — укажите обязательно его, например, http://127.0.0.1:8080). Нажмите Validate.

Результаты проверки Xdebug

Если у Вас результат проверки выглядит так же, то Вы все настроили правильно. Закройте окно Validate Debugger Configuration.

Если у Вас проблема с валидацией имени сервера, значит вы в настройках сервера nginx не указали 127.0.0.1 в качестве одного из server_name. Если проблема с загрузкой php.ini, значит вы использовали другой способ конфигурирования PHP вместо загрузки php.ini в каталог контейнера /usr/local/etc/php/ — это не страшно, если Вы понимаете, что делаете.

Добавить PHP сервер

Откройте настройки: «Languages & Frameworks» — PHP — Servers и нажмите плюсик, чтобы добавить новый.

Укажите имя сервера Docker (должно совпадать с переменной окружения PHP_IDE_CONFIG в docker-compose.yml) и хост 127.0.0.1. Затем ниже включите опцию Use path mappings и укажите соответствие между локальным каталогом проекта и этим же каталогом проекта внутри Docker-контейнера. Это соответствие изначально настраивается в docker-composer.yml для службы php-fpm в разделе volumes. Затем нажмите OK.

Новый PHP сервер с отображением локального каталога на каталог контейнера

Добавить конфигурацию для запуска

Остался последний штрих — добавление конфигурации запуска. Мы добавим простую Web-страницу.

В главном меню Run перейдите в Edit configurations…

В открывшемся окне нажимайте плюсик вверху слева и выбирайте PHP Web page. Укажите имя для данной страницы, например, WWW, затем укажите сервер, с которым связана эта страница (мы создали его на предыдущем шаге) и нажмите OK.

Конфигурация Web сервера для отладки

Упрощенный способ взаимодействия с контейнерами

Для управления контейнерами, вам придется использовать длинные команды вроде:

docker-compose up -d
docker-compose down -v --remove-orphans
docker-compose logs -f
и так далее...

Но это не самое сложное. Самое сложное заключается в том, что теперь для запуска тестов вам придется вызывать сценарии внутри контейнеров. Например, чтобы запустить конкретный набор тестов, вам придется ввести следующую команду:

docker-compose run --rm php-fpm vendor/bin/phpunit --testsuite unit

Постоянный ввод таких команд — вырабатывает хорошую привычку, но быстро надоест. Ведь это можно упростить!

Для этого создайте в корне проекта файл (например, с именем alias.bash), содержащий псевдонимы команд, а затем настройте PhpStorm таким образом, чтобы он подключал его при открытии терминала. В Git-репозитории, ссылка на который приведена в конце статьи, вы найдете все необходимые файлы из этой статьи, включая файл alias.bash, который уже содержит некоторые самые необходимые команды в более развернутом виде, который будет удобней для редактирования и поддержки.

Ваш файл alias.bash может, например, выглядеть следующим, упрощенным образом:

alias env-up='docker-compose up -d'
alias env-stop='docker-compose stop'
alias phpunit='docker-compose run --rm php-fpm vendor/bin/phpunit'

Псевдонимы могут работать с параметрами. Это видно в последнем псевдониме — сценарию phpunit передаются все параметры, с которыми был вызван псевдоним. Такие псевдонимы можно назвать своего рода прокси-командами.

Чтобы файл с псевдонимами применялся при открытии сессии терминала в PhpStorm, откройте настройки: Tools — Terminal и в поле Shell path введите:

/bin/bash --rcfile alias.bash
Настройка загрузки файла с псевдонимами при старте сессии оболочки в PhpStorm

Теперь откройте терминал в PhpStorm (Alt+F12). Если он уже был открыт, то нужно закрыть и открыть снова. Теперь попробуйте просто выполнить команду env-up и Вы увидите, что фактически выполнилась команда docker-compose up -d.

Вы можете составлять псевдонимы любой сложности, включая составные, состоящие из других псевдонимов. Это позволяет сосредоточиться на разработке, не отвлекаясь на составление команд для запуска чего либо внутри контейнера вручную.

Запуск отладки

Теперь пришло время приступить к самой отладке.

В подкаталоге проекта public создайте файл index.php с следующим содержимым:

<?php
$s = $_SERVER;
phpinfo();

Вверху справа на панели запуска выберите созданную конфигурацию и нажмите зеленый треугольник.

Это действие должно запустить сервер, который откроет в браузере файл index.php, в результате чего в браузере должен быть отображен результат работы phpinfo().

Результат работы index.php через браузер

Отладка WEB-приложения

Устанавливаем точку останова на строку phpinfo();

И вместо зеленого треугольника нажмем кнопку с зеленым жуком — это запустит сервер в режиме отладки.

При первом запуске отладки в браузере вам возможно понадобится специальное расширение JetBrains IDE Support, которое обычно предлагается к установке автоматически. Установите его.

При запуске отладки, откроется браузер с URL, к которому будет добавлен параметр с идентификатором сессии отладки, что-то вроде: http://127.0.0.1/?XDEBUG_SESSION_START=11223

При этом вывод в браузер не будет выполнен, так как сработает точка останова. Вернитесь в окно PhpStorm. Активная точка останова выглядит вот так:

Активная точка останова

В данном случае птичкой обозначается та точка (а их может быть несколько), на которой произошел останов. Все переменные выше будут подсвечены их значениями для удобства.

Теперь переместимся ниже — на панель отладки.

Панель отладки PhpStorm

На ней видна вся отладочная информация. Справа — значения всех переменных в текущей области видимости (здесь также видна наша переменная $s), слева виден стек вызовов (сейчас он пуст, так как у нас примитивный пример). Имеются кнопки на левой панели, при помощи которых можно перезапустить отладку, прервать отладки продолжить выполнение с этого места, остановить отладку и другие. На верхней панели имеются кнопки, при помощи которых можно управлять отладкой: проходить пошагово каждое действие, начиная с точки останова, перепрыгивать функции, не заходя во внутрь и т.д.

Также существует другой способ отладки — при помощи «прослушивания». Нажмите кнопку с телефонной трубкой, чтобы активировать режим прослушивания.

После того, как кнопка изменит вид (с красного запрещающего знака на зеленые полоски), Вы можете просто открыть веб-приложение в браузере и, если в коде проставлены точки останова, режим отладки запустится автоматически.

Отладка консольного приложения (CLI)

Для отладки консольного приложения можно нажать кнопку с трубкой, чтобы включить режим прослушивания, как и в описанном выше способе.

Теперь можно запустить консольный скрипт следующей командой:

$ docker-compose exec php-fpm php ./public/index.php

Также можно создать псевдоним для вызова php-сценариев и поместить его в файл alias.bash. Например, вот такой:

alias php='docker-compose exec php-fpm php'

И теперь тоже самое можно будет сделать выполнив следующую команду:

php ./public/index.php

Если при этом отладка не сработала, убедитесь, чтобы имя сервера, которое создавали выше, совпадало с именем сервера в файле docker-compose.yml в переменной окружения PHP_IDE_CONFIG.

После окончания отладки не забудьте отключить режим прослушивания.

Отладка тестов (PhpUnit)

С отладкой тестов дела обстоят точно так же, как и с консольным приложением. Включайте прослушивание (кнопку с трубкой) и запускайте тесты следующим псевдонимом (прокси-командой):

$ phpunit

Но используя PhpStorm намного удобней и практичней использовать встроенные средства запуска тестов, которые предоставляют удобную навигацию по тестам, а также множество других полезных функций. Для того чтобы запустить тестирование средствами PhpStorm, необходимо настроить тестовый фреймворк.

Откройте настройки: «Languages & Frameworks | PHP | Test Frameworks». И добавьте новый фреймворк (нажмите кнопку с плюсиком) — PHPUnit by Remote Interpreter. В открывшимся окне выберите наш интерпретатор и нажмите ОК.

Настройка интерпретатора для PHPUnit

Затем необходимо указать путь к phpunit.xml, убедиться, что путь к Composer autoloader автоматически добавился (добавить вручную, если нет) и нажать ОК.

Настройка PHPUnit

Тестовый фреймворк настроен. Теперь необходимо добавить конфигурацию запуска тестов, подобно той, которую мы добавляли чуть выше для запуска веб-приложения.

Для этого в главном меню Run перейдите в Edit configurations…

Нажмите плюсик и добавьте PhpUnit. Укажите удобное Вам имя, например Tests и установите переключатель запуска в Defined in the configuration file. Теперь Вы можете настраивать тесты через конфигурационный файл phpunit.xml.

Конфигурация запуска тестов PHPUnit

Теперь точно так же кнопкой с зеленым треугольником на панели запуска Вы можете запускать тесты в обычном режиме, кнопкой с жуком — запускать тесты в режиме отладки и кнопки с щитом запускать тесты в режиме расчета покрытия тестами (для расчета покрытия Вы должны настроить блок whitelist в конфигурационном файле phpunit.xml).

При этом активировать режим прослушивания (кнопка с трубкой) уже не нужно.

Резюме

В данной статье был описан простой способ настройки и использования отладчика Xdebug в PhpStorm, если используется удаленный интерпретатор в виде Docker-контейнера.

Все файлы можно скачать на GitHub:
https://github.com/denisbondar/docker_php-fpm_xdebug

После того, как Вы склонируете себе репозиторий, не забудьте выполнить composer install для установки зависимостей и создания автозагрузчика. Для этого можно воспользоваться уже входящим в набор псевдонимов псевдонимом composer:

$ composer install

PhpStorm + Docker + Xdebug: 57 комментариев

  1. На шаге «Добавить внешний интерпретатор» у меня зависает phpstorm при окошке «checking php installation»

Прокомментировать