PHP: Настраиваем отладку. PhpStorm + PHP 8 + Docker + Xdebug 3
В очередной раз споткнувшись о собственную забывчивость, решил накидать небольшую заметку о том, как настроить отладку PHP в докере через xdebug для IDE PhpStorm. Кто-то правильно скажет, что в сети полно статей на данную тему, однако, все они не смогли спасти меня от совершения пары ошибок на которые я уже наступал ранее.
Итак, имеем следующую конфигурацию: на нашем Linux хосте есть IDE PhpStorm 2020.3, докер образ PHP версии 8.0.0, настраиваемый в контейнере через docker-compose и установленное в том-же образе расширение для отладки xdebug версии 3.0.1. Мы хотим заниматься отладкой в любимой IDE отлавливая вызовы как через веб-браузер, так и через контейнер, к примеру перехватывая из него запуски каких-либо CLI скриптов.
Первым делом читаем вот эту заметочку о том, как нам заставить работать отладку для скриптов запускаемых на удаленной стороне. Осмыслив суть, добавляем в настройку нашего контейнера с образом PHP (у меня настройки в docker-compose.yml) следующую строчку с переменной окружения PHP_IDE_CONFIG:
Здесь важно запомнить первый момент, что далее, при настройке PhpStorm, мы должны в точности повторить имя удаленного сервера для связки (маппинга) наших локальных исходников с исходниками на сервере. В нашем случае это имя задано как Docker. Это важный момент, не промахнитесь с регистром.
Далее, через php.ini, настраиваем xdebug, используя укороченный синтаксис от третьей версии. У меня настройки выглядят вот так:
Здесь скрыт второй важный момент, который заключается в том, что необходимо правильно указать параметр xdebug.client_host, который сообщает нашему расширению xdebug на каком адресе нас ждет слушатель для передачи отладочных данных. В моем случае, так как в контейнере я использую networking по умолчанию (т.е. у меня попросту нет настраиваемой секции networks) это будет host.docker.internal, который для Linux по умолчанию поднимается на интерфейсе 172.17.0.1. Это второй, очень важный момент.
Дальше, проще. Проходимся по стандартному пути настройки PhpStorm. Первым делом настраиваем PHP сервер, с которым мы будем общаться, вспоминая, что он находится на удаленной среде и для нашей IDE мы назвали его Docker. Таким образом, заходим в настройки PhpStorm (Ctrl+Alt+S) и по пути Languages & Frameworks > PHP > Servers добавляем вот такую конфигурацию:
В поле Name вводим настроенное ранее через PHP_IDE_CONFIG имя нашего сервера, т.е. Docker (см. п. 1). Поле Host содержит имя хоста, у меня оно не заполнено и поэтому используется символ подчеркивания (см. п. 2). Пункт 3 является важной составляющей связки локальных файлов с файлами сервера. Я связал свои локальные файлы, расположенные в /home/___/projects/www/___/www с файлами в контейнере, расположенными по пути /var/www/html.
Здесь мы даем произвольное осмысленное имя нашему интерпретатору (см. п. 1). В п.2 выбираем наш созданный ранее сервер Docker. Далее, в п.3 выбираем расположение конфигурационного файла docker-compose.yml и п.4 уточняем используемый сервис с PHP (у меня в docker-compose.yml он называется php). Если контейнер запущен и мы правильно всё выбрали, то в разделе General должна появиться информация о рабочем PHP окружении (см. п. 5).
На всякий случай проверяем настройки в Languages & Frameworks > PHP > Debug. Здесь важно, что мы будем слушать приходящие в IDE запросы на порту 9003 (это теперь порт по умолчанию в xdebug 3).
Всё, мы готовы к работе. Нажимаем заветную кнопочку Start Listening for PHP Debug Connections. Ставим точку останова в нужном месте PHP скрипта и дергаем его из браузера или прямо из докера. Вуаля, получаем, желаемое:
Понимаю, что многие, понятные для меня моменты я мог упустить, но общий минимальный порядок действий для запуска отладки я изложил полностью, если есть вопросы и предложения, милости просим в комментарии. Удачной отладки!
Дебажим PHP-контейнер с помощью Xdebug и PhpStorm
Перевод статьи подготовлен в преддверии старта курса «Backend-разработчик на PHP».

Инструкция Docker #9: xdebug
Я создам очень простую php-страницу и подебажу ее с помощью xdebug и PhpStorm.
Исходные файлы можно найти здесь:
github.com/ikknd/docker-study в папке recipe-09
1. Создайте файл «Dockerfile» в папке «docker»:
Выполните эту команду из папки docker для создания образа:
2. Создайте файл docker-compose.yml в папке «docker»:
Здесь я использую образ «php-xdebug-custom» вместо «php:7.2-fpm „
3. Внесите в файл php.ini следующие настройки:
4. Настройте сервер в PhpStorm:
5. Настройте удаленный дебагер PHP в PhpStorm:
Добавьте новую конфигурацию и присвойте ей значения, как на следующем скриншоте:
6. Выберите конфигурацию дебага на панели дебага PhpStorm
7. Перейдите в /var/www/docker-study.loc/recipe-09/docker/ и выполните:
Я могу установить точку останова, начать прослушивание соединений в панели дебага PhpStorm и перезагрузить страницу.
Как подружить PHPstorm, xDebug и удаленные ветки, собранные через Docker? Слишком просто…
Доброго времени суток, Хабр!
Еще год назад мой процесс отладки кода в PHP заключался в двух строчках:
Периодически, конечно, приходилось использовать более «сложные» конструкции:
Нет, что вы! Я знал — в наше время не подобает культурному программисту заниматься этим
Но, честно говоря, я всегда боялся того, что не понимаю. В том числе и принтеров xDebug, в особенности, как все это дело настроить. В один прекрасный день у меня получилось это сделать на своей машине и в локальном проекте — радости не было предела. Спустя много месяцев я столкнулся с новой проблемой, как заниматься отладкой в PHPstorm через xDebug, если проект собирается удаленно докером через CI.
Если Вы так же, как и я, испытываете трудности с настройкой разных штук, добро пожаловать под кат, я расскажу о своем опыте настройки окружения отладки с такими страшными словами, как Docker, xDebug, CI.
Для тех, кто не любит воду и хочет перейти непосредственно к сути настройки.
Почему стоит уйти от заплесневевших методов отладки и перейти на адекватные технологии?
Над катом я немного слукавил, я занимался кустарной отладкой не только потому, что боялся настроить что-либо, и не потому что слишком глупый, а просто потому, что у меня не было необходимости в чем-то более удобном. Чаще всего я работал над проектами, локально на своем довольно мощном компьютере, да и задачи были не настолько сложными, чтобы процесс отладки начал занимать достаточно весомую позицию.
В какой-то момент, я для себя осознал, что мне уже просто неудобно, и попытался подружить xDebug и PHPstorm при работе над локальным проектом. Беда в том, что большинство документаций и гайдов, которые я нашел, подразумевают, что читающий их человек довольно хорошо разбирается в предметной области и все понимает, в моем случае это было не так и на свою первую настройку xDebug я в сумме потратил 4-5 часов за 2 вечера. Это было довольно тяжело морально, я чувствовал себя бесконечно тупым. Тем не менее, настроить получилось, все работало!
Да, стало удобней, локально-дома, но на основной работе я занимался сайтами удаленно, и чаще всего у меня не было возможности либо выгрузить сайт локально (из-за слабой машины или неудобного процесса развертывания), либо влиять на настройки сервера из-за хостингов, поэтому вносил правки «на живую» и отладкой занимался через html-комментирование print_r (на той работе это было «нормально», хоть и не горжусь этим опытом).
Однако, 3 месяца назад я перешел в более крутую компанию и стал заниматься действительно серьезным проектом с высокой нагрузкой. И тут для меня многое изменилось. Инфраструктура и процесс разработки примерно такие: есть свой сервер GitLab, у каждого проекта есть свой репозиторий, в Jira приходят задачи, по номерам задачи создаешь ветку, при создании ветки с помощью CI автоматически создается своя песочница с сайтом, где ты спокойно работаешь, каждый push пересобирает ветку, по окончанию работ отдаешь на код-ревью, вливаешь ветку в мастер.
Все круто за исключением одного НО, каждый пересбор ветки в моем случае занимает примерно 10 секунд. В процессе самой разработки это несущественное время, так как я уже перешел ту стадию, когда приходилось проверять работоспособность кода чуть ли не каждую строчку из-за неуверенности и малого опыта. Однако, когда я переходил к отладке, эти 10 секунд начали играть ощутимую роль. Процесс подобной отладки выглядел в итоге так:
В какой-то момент мне это надоело и я решил-таки настроить xDebug, чтобы процесс отладки стал менее затратным. К несчастью, мои нынешние коллеги либо не пользовались этой технологией (жду шутки про «Устроился в крутую компанию, где никто не пользуется xDebug’ом»), либо не знали\не помнили, как подружить IDE с xDebug’ом, в случае когда ветка собирается удаленно через CI, а так как я ни разу не devOps и как я упомянул выше, процесс настройки чего-либо является для меня достаточно мучительным процессом, это вылилось примерно в 6 часов чистого времени, чтобы наконец все заработало, и я понимал процесс, и это было бы достаточно удобно.
Процесс настройки
Я не буду вдаваться в подробности, как прикручивать CI, Docker, в общем, как собрать инфраструктуру, предполагается, что это уже все готово и осталось только настроить свое личное окружение.
Допустим, наш репозиторий имеет примерно такую структуру:
Для начала нам нужно проверить, есть ли в текущем образе сам xDebug, для этого можете воспользоваться phpinfo();
Если xDebug уже включен в сборку — отлично, если нет, то ознакомьтесь с этим источником, который помог мне непосредственно в самой настройке, однако я пошел немного по другому пути.
Настраиваем php.ini
Для того, чтобы в итоге все заработало, нам важны 2 настройки xDebug:
Создаем в нашей ветке свои дополнительные настройки через тот же файл php-settings.ini, а расположим его в ./build_env/php/php-settings.ini
Прописываем в нем 2 вышеупомянутые настройки:
xdebug.remote_enable = on
xdebug.remote_host = IP.ВАШЕГО.КОМПЬЮТЕРА.ВСЕТИ
Примерно так в итоге выглядит docker-compose.tmpl в моем проекте:
При следующей сборке ветки, можно проверить привязались ли новые настройки через тот же phpinfo();, если да — отлично, если нет — Вам не повезло и придется пройти тот же путь, что проделал я в первый раз 🙁
Настраиваем маппинги в PHPstorm
Далее нужно настроить сам PHPstorm. Я решил не использовать DBgp Proxy, чтобы не настраивать маппинги во всплывающем окне каждый раз. В моем случае я использую шаблон сервера, который будет содержать в себе необходимые маппинги.
Переходим в Settings / Preferences | Languages & Frameworks | PHP | Servers
Сохраняем эти настройки, менять мы их будем каждый раз, когда работаем с новой веткой. Например, если сегодня работаю с веткой web-2233 то поменяю маппинг на /var/www/builds/путь_до_билда/web-2233
Добавляем новую переменную окружения, чтобы IDE автоматически подтягивала маппинги
Теперь довольно важный и не самый очевидный момент. Когда мы начинаем дебаг, PHPstorm должен понимать, какие локальные файлы соответствуют файлам на удаленном сервере. Если сервер не передал ему конкретную установку, то появится всплывающее окно, в котором нужно проставить соответствия путей вручную. Для того, чтобы PHPstorm сразу брал маппинги от сервера с названием BRANCH нужно добавить в нашу сборку переменную окружения PHP_IDE_CONFIG
В .gitlab-ci.yml задаем эту переменную:
— export PHP_IDE_CONFIG=»serverName=BRANCH»
Готово!
Нам не нужны расширения для браузера, не нужно передавать в get-параметры URL XDEBUG_SESSION_START=IDE_KEY, не нужно добавлять дополнительные конфигурации.
Достаточно просто включить прослушку 
Спасибо за внимание, надеюсь эта статья будет полезна и кто-нибудь сэкономит время, не наступая на те же грабли, что и я 🙂
Настройка Xdebug в PhpStorm
Настройка Xdebug в PhpStorm
1. Отладка на локальном компьютере
Все настройки будут показаны на примере Ubuntu и интерпретатора PHP, настроенного вместе с Apache. Для других конфигураций пути к файлам могут отличаться, но суть останется та же. Давайте разберемся как это будет работать. Отладчик Xdebug позволяет приостанавливать выполнение кода, смотреть стек вызовов и содержимое переменных. Однако удобного интерфейса управления у него нет. Поэтому для управления отладкой будем использовать IDE. Но IDE не может никак сообщить отладчику что надо запустить отладку, потому что она отвечает только за код. Для этого понадобится ещё расширение для браузера Debug Helper, с помощью которого вы сможете включить режим отладки.
Сначала необходимо установить Xdebug. Для этого выполните:
sudo apt install xdebug
После завершения установки Xdebug надо настроить программу так, чтобы при запуске сеанса отладки она пыталась подключится к нашей IDE для управления отладкой. Для этого добавьте такие строчки в файл /etc/php/7.4/apache2/conf.d/20-xdebug.ini в версии Xdebug 2:
sudo vi /etc/php/7.4/apache2/conf.d/20-xdebug.ini
zend_extension=xdebug.so
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
xdebug.remote_connect_back=1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_autostart=false
Для новой версии Xdebug 3 старая конфигурация тоже будет работать, но лучше использовать новый стандарт:
xdebug.mode=debug
xdebug.start_with_request=trigger
xdebug.discover_client_host = false
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9000
Если необходимо, вы можете включить несколько режимов, перечислив их через запятую. Вторая строчка xdebug.start_with_request определяет как будет запускаться отладчик для режимов debug, trace и им подобных:
Если третья строка xdebug.discover_client_host имеет значение true, то xdebug пытается подключится к хосту, переданному в заголовке HTTP_X_FORWARDED_FOR. Но так как хост указывается в следующей строке, в данном случае эта возможность не нужна. Дальше указывается хост клиента, куда надо подключится для управления отладкой 127.0.0.1 и порт 9000. Именно этот порт используется в PhpStorm по умолчанию на данный момент. После сохранения настроек перезапустите Apache:
sudo systemctl restart apache2
Нажмите кнопку + и в открывшемся списке выберите PHP Remote Debugger:
В настройках можно ничего не менять, укажите только имя этого способа отладки. Если сервер, с которого будут ожидаться подключения не задать, то будут приниматься все подключения.
Теперь IDE готова. Кликните по значку жука на верхней панели инструментов чтобы программа начала ожидать подключения отладчика и поставьте несколько точек останова в коде просто кликнув перед строчкой с кодом:
Дальше осталось настроить браузер. Для Chrome можно скачать это расширение. Установите его и откройте страницу, которую надо отлаживать. Кликните по значку расширения и выберите Debug. Значок расширения станет зеленым:
Обновите страницу и возвращайтесь к PHPStorm. Если всё было сделано верно, там запустится сеанс отладки. При первом запуске программа может попросить настроить соответствия локальных путей к файлам с удаленными. Для локального сервера здесь ничего настраивать не надо, достаточно нажать Accept:
Дальше отладчик остановит выполнение на выбранной точке останова и в программе появится интерфейс отладки:
Как видите всё довольно просто. Дальше давайте разберемся как настроить Xdebug PhpStorm и Docker.
2. Отладка Php в Docker
C Docker возникает одна сложность. Поскольку отладчик должен сам подключится к IDE, необходимо пробросить хост в контейнер. В windows это можно сделать с помощью адреса host.docker.internal. Но в Linux, по умолчанию это не происходит. Для того чтобы добавить такой адрес надо добавить в docker-compose такие строчки:
Давайте для этого примера будем использовать такую структуру директорий:
Минимально необходимый docker-compose.yaml будет выглядеть вот так:
Здесь объявляется два контейнера, nginx и php-fpm. В первый не надо вносить никакие изменения, поэтому можно просто брать официальный образ, монтировать папку с исходниками, конфигурационный файл и пробрасывать порт. Во второй контейнер надо установить и настроить Xdebug поэтому его придется собрать на основе официального контейнера php. Для этого же контейнера надо указать директиву extra_hosts, без неё ничего работать не будет. Конфигурация Nginx тоже вполне стандартная:
FROM php:8.0.3-fpm-buster
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
COPY xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
Здесь устанавливается xdebug с помощью pecl, а затем копируется его конфигурационный файл в контейнер. Вот этот конфигурационный файл:
xdebug.mode=debug
xdebug.start_with_request=trigger
xdebug.discover_client_host = false
xdebug.client_host = host.docker.internal
xdebug.client_port = 9000
В контейнере установлен Xdebug 3 для PHP 8, потому что старая версия с этой версией языка уже не работает, поэтому используется новый синтаксис конфигурации. Ну и хост, host.docker.internal, который раньше был прописан в docker-compose.yaml. Настройка xdebug PHPStorm docker завершена. Дальше вы можете запустить проект:
Теперь, как и в предыдущем варианте вы можете включить в браузере режим отладки, обновить страницу и отладчик успешно подключится к IDE, не смотря на то, что он работает в Docker:
Выводы
В этой статье мы рассмотрели как выполняется настройка xdebug PHPStorm на локальном компьютере и в Docker контейнере. Если всё сделать правильно отладчик будет работать и помогать вам искать ошибки в коде.
Docker + php-fpm + PhpStorm + Xdebug
Не так давно тимлид нашей команды сказал: ребята я хочу, чтобы у всех была одинаковая среда разработки для наших боевых проектов + мы должны уметь дебажить всё — и web приложения, и api запросы, и консольные скрипты, чтобы экономить свои нервы и время. И поможет нам в этом docker.
Поэтому, давайте уточним условия задачи:
На боевых серверах используется стандартная связка nginx + php-fpm + mysql. И, в чем проблема?
Разворачиваем на локальной машине точно такое же окружение + Xdebug, настраиваем наши проекты в PhpStorm, работаем. Для дебага включаем «трубку» в PhpStorm, всё работает из коробки, всё замечательно.
Всё это действительно так — всё работает из коробки. Но, давайте попробуем заглянуть под капот нашего рабочего окружения.
Nginx + php-fpm общаются через сокет, xdebug слушает порт 9000, PhpStorm тоже, по умолчанию, слушает порт 9000 для дебага и всё вроде бы замечательно. А если у нас открыто несколько приложений в PhpStorm, и включена прослушка («трубка)» для нескольких приложений? Что сделает PhpStorm? Он начнет ругаться, что обнаружено новое подключение для Xdebug, вы хотите его игнорировать, или нет?
Т.е., при настройках по умолчанию в PhpStorm, в конкретный момент времени, я могу дебажить только одно приложение. У всех других открытых приложений дебаг должен быть выключен. Блин, но это же неудобно. Я хочу слушать для дебага все приложения, и если в каком-то из них стоит точка останова, то я хочу, чтобы PhpStorm остановился в этом приложении, на той строке, где мне нужно.
А что для этого нужно? А нужно, чтобы каждое приложение запускалось со своими настройками для Xdebug. Чтобы каждое приложение слушало свой порт, искало свой сервер, а не так, как у нас всё общее, всё в одной куче.
А для этого есть замечательный докер! Мы можем запустить каждое наше боевое приложение в отдельном контейнере, на основе одного общего образа, например, php:7.1-fpm. Благодаря технологии докера мы можем изолировать наши приложения, при минимальных накладных расходах.
Ок, давайте заведем наши боевые проекты под докер, запустим каждый проект в отдельном контейнере, настроим каждый проект в PhpStorm для дебага индивидуально, всё должно быть замечательно.
Костылим, вот пример образа php:7.1-fpm, который будет собираться.
Update
Как справедливо указало сообщество — совсем уж жестко костылить всё-таки не надо.
Например хаброюзер 1ntrovert в своем комментарии
Поправленный пример Dockerfile
При запуске контейнера из данного образа пользователь www-data получает uid=1000, gid=1000. Обычно такие права у первого созданного пользователя в операционной системе Линукс. И, именно с такими правами будут работать наши php-fpm контейнеры. Буду очень благодарен, если кто-то подскажет как можно работать без костылей с правами доступа в докер.
При запуске контейнера из данного образа пользователь www-data получает uid и gid, которые будут переданы извне.
Также в комментариях поднималась тема: зачем вообще менять права пользователю www-data, чем не устраивают стандартные права 33. Только одним: когда мы зайдем в контейнер, и создадим, например, файл миграции, то на машине хоста владельцем этого файла будем не мы. И каждый раз надо будет запускать что-то вроде
И вторая небольшая проблема: для корректной работы Xdebug необходимо прописать верный ip адрес для машины хоста. У каждого члена команды он разный. 127.0.0.1 не катит. И тут нам на помощь приходит сам докер. Например, мы можем явно сконфигурировать сеть — 192.168.220.0/28. И тогда наша машина всегда будет иметь адрес 192.168.220.1. Этот адрес мы будем использовать как для настройки PhpStorm, так и для настройки других приложений. Например, при работе с MySql.
Сам docker-compose.yml, после учета замечаний, выглядит так:
Мы видим, что в данном конфиге создаются два контейнера php71-first и php71-two, на основе одного образа php:7.1-fpm. У каждого контейнера свои настройки для Xdebug. Каждый отдельно взятый контейнер будет слушать, для дебага, свой порт и свой сервер.
Также, обращаю ваше внимание на директивы
Без этих переменных образ php-fpm не запустится. Вопрос: как их передать в docker-compose.yml? Ответ: так как удобнее вам. Можно при запуске:
Полный код для демо версии выложен на гитхабе.
Листинг демо проекта:
Пробежимся, кратко, по листингу проекта.
Каталог hosts — конфигурационные файлы для Nginx. В каждом конфиге прописан свой контейнер php-fpm. Пример:
Каталог images — инструкции для сборки образов php-fpm, каталог mysql — храним базы, каталог www — все наши web проекты, в нашем примере first.loc и two.loc.
Давайте подведем промежуточные итоги: используя возможности докера мы запустили все свои рабочие проекты в одном окружении. Все наши проекты видят друг друга, для каждого из проектов прописаны уникальные настройки для Xdebug.
Осталось корректно настроить PhpStorm для каждого из проектов. При настройке мы должны прописать порт для дебага и имя сервера в нескольких местах.
Создаем проект в PhpStorm
Настраивать будем разделы меню
— PHP (необходимо верно прописать CLI Interpreter),
— Debug (меняем порт на 9008, как в файле docker-compose.yml),
— DBGp proxy (IDE key, Host, Port),
update Спасибо хаброюзеру CrazyLazy за важное замечание. Пункт меню DBGp proxy настраивать не надо.
— Servers (необходимо верно указать имя сервера, как в файле docker-compose.yml, и use path mappings)
Все дальнейшие скрины буду прятать под спойлер.
Хитрого ничего нет — важно, при настройке выбрать нужный образ, и верно прописать имя сервера. По умолчанию имя сервера Docker, у нас оно своё.
Опять же всё прописываем из настроек docker-compose.yml для конкретного контейнера. На этом же шаге валидируем как работает наш дебаг.
Важно правильно прописать use path mappings, имя сервера опять же берем из настроек
Сервер выбираем наш, созданный на предыдущем шаге.
Ну, собственно и всё. Написано много букв, вроде бы всё непросто
На самом деле — главное понять очень простую вещь. Благодаря технологии докера мы можем запустить все наши рабочие приложения в едином пространстве, но с разными настройками для Xdebug. Каждое приложение работает в своем контейнере, и нам остаётся аккуратно прописать настройки для каждого приложения в PhpStorm.
И на выходе мы получаем чудесную картину.
2. Прописываем узлы first.loc и two.loc в файле /etc/hosts
4. Настраиваем оба проекта first.loc и two.loc в PhpStorm, так как описано выше, и запускаем оба проекта в PhpStorm. Т.е. у нас открыто два окна PhpStorm, с двумя проектами, каждый из них слушает входящие соединения (трубка включена).
5. В проекте two.loc ставим точку останова на второй, например, строке. В первом проекте first.loc запускаем http запрос из файла http.http
И о чудо! Нас перекидывает во второй проект, на нашу точку останова.
Для дебага консольных скриптов делаем всё ровно тоже самое. Включаем трубку для прослушки, ставим точку останова, заходим в нужный контейнер, запускаем нужный скрипт.
Где php71first — алиас на машине хоста:
cdf — алиас, который работает в контейнере. Выше я писал о том, что для общения с контейнерами предпочитаю использовать алиасы.
На этом всё, конструктивная критика, замечания приветствуются.




















