PhpStorm + Docker + Xdebug

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

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

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

У вас должны быть установлены Linux, PhpStorm, Docker, Docker-compose. Если Вы ведете разработку не на 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.1-fpm

RUN apt-get update && apt-get install -y wget git unzip \
    && pecl install xdebug-2.6.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 нажмите кнопку с тремя точками.

Переход к настройке интерпретаторов

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

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

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

Docker
Docker Compose

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

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

Корректно добавленный интерпретатор из Docker

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

Выбран внешний интерпретатор Docker

Нажмите Apply.

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

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

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

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

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

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

Добавить 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 сервера для отладки

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

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

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

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

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

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

Результат работы phpinfo()

Отладка 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

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

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

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

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

$ docker-compose exec php-fpm ./vendor/bin/phpunit

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

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

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

Затем необходимо указать путь к autoload.php и путь к phpunit.xml и нажать ОК.

Настройка 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 для установки зависимостей и создания автозагрузчика.

$ docker-compose exec php-fpm composer install

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

  1. Добрый день. Спасибо за статью.
    Вроде бы все настроено правильно. Xdebug работает если запускать php-скрипт.
    Но не работает в режиме прослушки как бы я его не крутил с расширением и без, с параметром XDEBUG_SESSION_START, такое чувство что вообще не хочет слушать сайт или ему кто-то мешает это делать. Куда копать не понимаю

    1. Добрый день. Пожалуйста.
      Добавьте логгирование в xdebug.
      Для этого в файл docker-compose.yml в значение параметра XDEBUG_CONFIG дополнительно добавьте через пробел:
      remote_log=/var/www/xdebug.log
      И пересоберите контейнеры:
      docker-compose up --build -d
      В корне проекта должен будет появиться файл xdebug.log, в котором, скорее всего, и найдется ответ.

      1. Не очень понятно , но отладка сайта заработала после того как посмотрел ip докера через ipconfig и дописал его в docker-compose.yml remote_host=172.17.0.1.
        Но теперь не могу настроить маппинг, хотя сопоставления правильное

        1. remote_host должен быть первым из диапазона хостов, указанных в networks файла docker-compose.yml. Если, конечно, у вас не Windows.
          Про маппинг не подскажу. Ищите ошибку. Возможно где-то поспешили и недочитали.

  2. Спасибо за статью. Набрел на нее в поисках ответа на вопрос, который лишь косвенно связан с содержимым статьи.

    xdebug у вас работает на 9000 порту.
    Но в docker-compose нет маппинга для этого порта. Согласно документации docker не должен давать пройти пакету из контейнера с php на хостовую систему, если нет настроенного маппинга.

    Но если бы вы добавили маппинг 9000:9000 для php-fpm, то возникла бы другая проблема, т.к. phpStorm у вас слушает тоже на 9000 порту. Нельзя использовать один и тот же порт разными утилитами одновременно, пришлось бы маппить 9000 порт из контейнера на другой порт (например 9001) на хосте и настраивать прокси через конфиг DBGProxy в том же phpStorm’е.

    НО! У вас все работает. И в общем-то у меня те же результаты без настройки маппинга, xDebug работает прекрасно без проброса 9000 порта на хостовую машину! Как так? Может быть вы исследовали этот вопрос? По какому пути идет пакет от xDebug до 9000 порта на хостовой машине?

    1. Добрый день. Всегда пожалуйста.
      Маппинг не нужен по той причине, что не шторм подключается внутрь контейнера, а наоборот — xdebug из контейнера подключается к внешнему узлу — хосту, на которому работает шторм, который и принимает соединение. Все исходящие соединения из контейнера наружу всегда разрешены и не требуют маппинга. Даже, в данном случае, это не соединение наружу, а соединение в пределах широковещательного домена локальной сети. Хост и контейнер находятся внутри одного широковещательного домена (локальной сети) и могут беспрепятственно общаться друг с другом точно так же, как это делают контейнеры между собой. Маппинг нужен чтобы «пробросить» порт наружу — за пределы бандла.

  3. Только вот в XDEBUG_CONFIG нужно еще добавить remote_connect_back=1, чтобы заработал дебаггер адекватно, иначе ни xdebug ни jb ide helper не помогают

    впрочем настраивать сервер в IDE не обязательно, как и накатывать jetbrains ide helper
    достаточно подрубить пыху в IDE, в расширении xdebug включить режим отладки и в ide тоже

    тогда все запросы будут перехватываться по брекпоинту, даже аякс

    1. Спасибо за комментарий!
      На самом деле, вовсе не обязательно добавлять remote_connect_back=1. Работает и без этого. Ну, или я не понял, что означает «чтобы заработал дебаггер адекватно». Поясните, пожалуйста, разницу.

      Сервер в IDE настраивать обязательно, чтобы консольные приложения, включая тесты, запущенные в консоли, также можно было отлаживать. Это необходимо для настройки отображения каталогов на хосте и в контейнере. Про JetBrain Helper в браузере — соглашусь. Устанавливать не обязательно. Но рекомендуется. Кажется, работает и без установки этого плагина, но браузер будет каждый раз рекомендовать установить плагин.

  4. Вообще супер статья, автору +100500 в карму.
    У меня задача немного посложнее будет, думаю развернуть xdebag на удалённом сервере с использованием docker деплой кода(в моём случае тестов) будет осуществляться по средствам ssh (также должен являться контейнером)

    всё это после оберну в кубернетис и прикреплю к git CI.

  5. Очень круто! Я имею богатый опыт освоения новых инструментов и хорошо вникаю по английским мануалам. Но в этой связке я всю голову сломал от разного рода мелких нестыковок. По вашей статье я нашел все свои ошибки в настройках и сразу все заработало. Посему считаю этот материал отменным! В такие моменты, офигеваю от возможностей phpStorm, и он все-таки стоит своих денег =)))

  6. Благодарствую за статью.
    Перерыл кучу исходников с километровыми портянками, чувствую начал терять кучу времени.
    У Вас все просто и понятно.
    Не все пишут, но думаю, многие начинающие благодарны.

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