PhpStorm, Docker и Xdebug 3 на PHP 8.1 в 2022 году [Учебник Часть 3]

Эта статья впервые появилась на сайте https://www.pascallandau.com/ в разделе PhpStorm, Docker и Xdebug 3 на PHP 8.1 в 2022 году [Учебник, часть 3].


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

Все примеры кода находятся в открытом доступе в моем репозитории Docker PHP Tutorial на Github. Вы найдете ветку для этого руководства по адресу part-4-2-phpstorm-docker-xdebug-3-php-8-1-in-2022.

Все опубликованные части Docker PHP Tutorial собраны на специальной странице Docker PHP Tutorial. Предыдущая часть – Docker с нуля для приложений PHP 8.1 в 2022 году, следующая – Запуск Laravel 9 на Docker в 2022 году.

Если вы хотите следить за развитием событий, пожалуйста, подпишитесь на RSS-ленту или электронную почту, чтобы получать автоматические уведомления о выходе следующей части 🙂

Оглавление

  • Введение
  • Установка инструментов
    • Установите composer
    • Установите Xdebug
    • Установите PHPUnit
    • Установите SSH
  • Настройка PhpStorm
    • Конфигурация SSH
    • Интерпретатор PHP
    • PHPUnit
    • Отладка
      • Отладка кода, выполняемого через PhpStorm
      • Отладка кода, выполняемого через php-fpm, cli или с рабочего сервера
        • php-fpm
        • cli
        • php-workers
  • strace
  • Подведение итогов

Введение

Эта статья в основном является обновлением статьи Настройка PhpStorm с Xdebug для локальной разработки на Docker, но мы также рассмотрим “оставшиеся случаи” отладки процессов php-fpm и php worker.

Мы по-прежнему будем полагаться на постоянно работающую установку docker, к которой мы подключаемся через SSH Configuration вместо использования встроенных возможностей docker-compose, поскольку я считаю, что это ближе к тому, что мы делаем в CI / production. Однако мы больше не будем использовать SSH-ключи, а просто будем аутентифицироваться по паролю. Это снижает сложность и устраняет любые назойливые предупреждения о том, что “SSH-ключи могут быть открыты в репозитории”.

Инструменты для установки

Установка composer Composer устанавливается путем извлечения официального образа composer docker и простого “копирования” исполняемого файла composer в базовый образ php. Кроме того, composer нуждается в расширениях mbstring и phar.

# File: .docker/images/php/base/Dockerfile

ARG ALPINE_VERSION
ARG COMPOSER_VERSION
FROM composer:${COMPOSER_VERSION} as composer
FROM alpine:${ALPINE_VERSION} as base

# ...

RUN apk add --update --no-cache  
        php-mbstring~=${TARGET_PHP_VERSION} 
        php-phar~=${TARGET_PHP_VERSION} 

# ...

COPY --from=composer /usr/bin/composer /usr/local/bin/composer
Вход в полноэкранный режим Выход из полноэкранного режима

Поскольку мы хотим, чтобы наша сборка была детерминированной, мы “привязываем” версию composer, добавляя переменную COMPOSER_VERSION в файл .docker/.env.

COMPOSER_VERSION=2.2.5
Вход в полноэкранный режим Выйти из полноэкранного режима

и использовать ее в .docker/docker-compose/docker-compose-php-base.yml:

services:
  php-base:
    build:
      args:
        - COMPOSER_VERSION=${COMPOSER_VERSION?}
Вход в полноэкранный режим Выход из полноэкранного режима

Установите Xdebug Установите расширение через apk (только для цели local):

# File: .docker/images/php/base/Dockerfile

FROM base as local

RUN apk add --no-cache --update 
        php-xdebug~=${TARGET_PHP_VERSION} 
    # ensure that xdebug is not enabled by default
    && rm -f /etc/php8/conf.d/00_xdebug.ini
Войти в полноэкранный режим Выйти из полноэкранного режима

Мы также не хотим включать xdebug сразу, а только когда нам это нужно (из-за снижения производительности, когда расширение включено), поэтому мы удалим конфигурационный файл по умолчанию и отключим расширение в файле приложения .ini.

# File: .docker/images/php/base/conf.d/zz-app-local.ini

; Note:
; Remove the comment ; to enable debugging
;zend_extension=xdebug
xdebug.client_host=host.docker.internal
xdebug.start_with_request=yes
xdebug.mode=debug
Вход в полноэкранный режим Выйти из полноэкранного режима

См. статью Исправление Xdebug в PhpStorm при запуске из контейнера Docker для объяснения настройки xdebug.client_host=host.docker.internal (ранее называлась xdebug.remote_host в xdebug < 3). Это будет работать из коробки для Docker Desktop, но для пользователей Linux нам нужно добавить магическую ссылку host-gateway во все контейнеры PHP (мы добавим ее во все контейнеры PHP).
во все контейнеры PHP (мы не можем добавить ее в базовый образ php, потому что это настройка времени выполнения):

services:
  service:
    extra_hosts:
      - host.docker.internal:host-gateway
Вход в полноэкранный режим Выйти из полноэкранного режима

Наконец, нам нужно добавить переменную окружения PHP_IDE_CONFIG
во все контейнеры PHP. Переменная определяется как PHP_IDE_CONFIG=serverName=dofroscra, где “dofroscra” – это имя сервера, который мы позже настроим для отладки. Поскольку нам нужно одно и то же значение в нескольких местах, переменная настраивается в .docker/.env:

PHP_IDE_CONFIG=serverName=dofroscra
Войти в полноэкранный режим Выйти из полноэкранного режима

А затем добавляется в .docker/docker-compose/docker-compose.local.yml.

services:
  php-fpm:
    environment:
      - PHP_IDE_CONFIG=${PHP_IDE_CONFIG?}

  php-worker:
    environment:
      - PHP_IDE_CONFIG=${PHP_IDE_CONFIG?}

  application:
    environment:
      - PHP_IDE_CONFIG=${PHP_IDE_CONFIG?}
Войти в полноэкранный режим Выйти из полноэкранного режима

Установка PHPUnit PHPUnit будет установлен через composer, но не будет “запечен в образ” для локальной разработки. Таким образом, мы должны запустить composer require в контейнере. Чтобы сделать это более удобным, в .make/01-00-application-setup.mk добавлена цель make для запуска произвольных команд composer:

.PHONY: composer
composer: ## Run composer commands. Specify the command e.g. via ARGS="install"
    $(EXECUTE_IN_APPLICATION_CONTAINER) composer $(ARGS);
Вход в полноэкранный режим Выход из полноэкранного режима

Это позволяет мне запустить make composer ARGS="install" с хост-системы, чтобы выполнить composer install в контейнере. В результате composer будет использовать версию PHP и расширения контейнера application для установки зависимостей, но я все равно буду видеть установленные файлы локально, потому что кодовая база настроена как том для контейнера.

Перед установкой phpunit мы должны добавить необходимые расширения dom и xml в контейнер

# File: .docker/images/php/base/Dockerfile

# ...

RUN apk add --update --no-cache  
        php-dom~=${TARGET_PHP_VERSION} 
        php-xml~=${TARGET_PHP_VERSION} 
Вход в полноэкранный режим Выйти из полноэкранного режима

а также перестроить и перезапустить установку docker с помощью команды

make docker-build
make docker-down
make docker-up
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь мы можем добавить phpunit через

make composer ARGS='require "phpunit/phpunit"'
Войти в полноэкранный режим Выйти из полноэкранного режима

который создаст файл composer.json и настроит каталог vendor/:

$ make composer ARGS='require "phpunit/phpunit"'
Using version ^9.5 for phpunit/phpunit
./composer.json has been created
Running composer update phpunit/phpunit
Loading composer repositories with package information
Updating dependencies
...
Вход в полноэкранный режим Выход из полноэкранного режима

ВНИМАНИЕ: Если на этом шаге вы столкнулись со следующей ошибкой разрешения, скорее всего, вы используете Linux и не установили переменные APP_USER_ID и APP_GROUP_ID, как описано в предыдущей статье в разделе Решение проблем с разрешениями.

make composer ARGS='req phpunit/phpunit' ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application application composer req phpunit/phpunit
./composer.json is not writable.
make: *** [.make/01-00-application-setup.mk:14: composer] Error 1
Вход в полноэкранный режим Выход из полноэкранного режима

Я также добавил

  • минимальный файл конфигурации phpunit.xml
  • тестовый пример по адресу tests/SomeTest.php
  • и новый Makefile для “всего, что связано с qa” по адресу .make/01-02-application-qa.mk:
##@ [Application: QA]

.PHONY: test
test: ## Run the test suite 
    $(EXECUTE_IN_WORKER_CONTAINER) vendor/bin/phpunit -c phpunit.xml
Вход в полноэкранный режим Выход из полноэкранного режима

Таким образом, я могу запускать тесты просто через make test.

$ make test
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application php-worker vendor/bin/phpunit
PHPUnit 9.5.13 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 00:00.324, Memory: 4.00 MB

OK (1 test, 1 assertion)
Войти в полноэкранный режим Выйти из полноэкранного режима

Установка SSH

Мы будем выполнять команды из PhpStorm по ssh в контейнере application. Как уже упоминалось, мы не будем использовать ключевой файл для аутентификации, а просто воспользуемся паролем, который задается переменной APP_SSH_PASSWORD в .docker/.env и передается образу в .docker/docker-compose/docker-compose.local.yml. Кроме того, мы сопоставляем порт 2222 из хост-системы с портом 22 контейнера приложения и убеждаемся, что кодовая база разделяется как том между хостом и контейнером.

  application:
    build:
      args:
        - APP_SSH_PASSWORD=${APP_SSH_PASSWORD?}
    volumes:
      - ${APP_CODE_PATH_HOST?}:${APP_CODE_PATH_CONTAINER?}
    ports:
      - "${APPLICATION_SSH_HOST_PORT:-2222}:22"
Войдите в полноэкранный режим Выйти из полноэкранного режима

Контейнер уже содержит openssh и устанавливает пароль

ARG BASE_IMAGE
FROM ${BASE_IMAGE} as base

FROM base as local

RUN apk add --no-cache --update 
        openssh

ARG APP_SSH_PASSWORD
RUN echo "$APP_USER_NAME:$APP_SSH_PASSWORD" | chpasswd 2>&1

# Required to start sshd, otherwise the container will error out on startup with the message
# "sshd: no hostkeys available -- exiting."
# @see https://stackoverflow.com/a/65348102/413531 
RUN ssh-keygen -A

# we use SSH deployment configuration in PhpStorm for local development
EXPOSE 22

CMD ["/usr/sbin/sshd", "-D"]
Войти в полноэкранный режим Выйти из полноэкранного режима

Настройка PhpStorm

Мы настроим удаленный интерпретатор PHP, который использует SSH-соединение для выполнения команд в контейнере application. До этого мы использовали конфигурацию SFTP Deployment configuration , которая была немного запутанной (“Что здесь делает SFTP?”), поэтому мы будем использовать SSH Configuration вместо этого и настроим сопоставление путей в интерфейсе интерпретатора Cli.

SSH-конфигурация

В File | Settings | Tools | SSH Configurations создайте новую SSH-конфигурацию под названием “Docker PHP Tutorial” со следующими настройками

  • Хост: 127.0.0.1
  • Порт: см. APPLICATION_SSH_HOST_PORT в .docker/docker-compose/docker-compose.local.yml
  • Имя пользователя: см. APP_USER_NAME в .make/.env
  • Тип аутентификации: Пароль
  • Пароль: см. APP_SSH_PASSWORD в .docker/.env

Интерпретатор PHP

В File | Settings | PHP добавьте новый интерпретатор PHP CLI, который использует новую конфигурацию SSH.

Кроме того, мы определяем путь к расширению xdebug, поскольку по умолчанию оно отключено, но PhpStorm может включить его автоматически, если потребуется. Вы можете найти путь в контейнере application через

root:/var/www/app# php -i | grep extension_dir
extension_dir => /usr/lib/php8/modules => /usr/lib/php8/modules
root:/var/www/app# ll /usr/lib/php8/modules | grep xdebug
-rwxr-xr-x    1 root     root        303936 Jan  9 00:21 xdebug.so
Войти в полноэкранный режим Выйти из полноэкранного режима

Нам все еще нужно закрепить Xdebug на PhpStorm при запуске из контейнера Docker, добавив пользовательскую опцию PHP для xdebug.client_host=host.docker.internal. Это же значение мы используем в .docker/images/php/base/conf.d/zz-app-local.ini.

Теперь в обзоре интерпретатора мы должны настроить сопоставление путей, чтобы PhpStorm знал, “какой локальный файл принадлежит какому удаленному”. Удаленная папка определяется в .docker/.env через

APP_CODE_PATH_CONTAINER=/var/www/app
Войти в полноэкранный режим Выйти из полноэкранного режима

После этого мы можем установить точку останова, например, в setup.php и начать отладку:

На скриншоте видно, что PhpStorm добавляет расширение Xdebug, которое мы определили ранее.

PHPUnit

phpunit настраивается через File | Settings | PHP | Test Frameworks. Сначала мы выбираем интерпретатор, который мы только что добавили.

Затем мы добавляем пути к скрипту автозагрузки composer и файлу конфигурации phpunit.xml.

Теперь PhpStorm будет выполнять тесты с использованием интерпретатора PHP в контейнере application.

Отладка

Прежде всего, если вы еще не сделали этого, пожалуйста, ознакомьтесь с официальной документацией по xdebug. Дерик проделывает огромную работу по подробному объяснению xdebug, включая некоторые полезные видео, например, Xdebug 3: Xdebug с Docker и PhpStorm за 5 минут.

Отладка кода, выполняемого через PhpStorm

Это уже должно работать из коробки. Просто установите точку прерывания, щелкните правой кнопкой мыши на файле и выберите “Debug ‘…'”.

Отладка кода, выполняемого через php-fpm, cli или с рабочего сервера

Для кода, который выполняется “напрямую” контейнером без PhpStorm, сначала нужно включить xdebug в контейнере, удалив ; перед расширением в /etc/php8/conf.d/zz-app-local.ini.

; Note:
; Remove the comment ; to enable debugging
zend_extension=xdebug
Вход в полноэкранный режим Выйти из полноэкранного режима

Чтобы сделать это немного удобнее, мы используем специальные рецепты make для этих действий в .make/01-01-application-commands.mk

.PHONY: execute-in-container
execute-in-container: ## Execute a command in a container. E.g. via "make execute-in-container DOCKER_SERVICE_NAME=php-fpm COMMAND="echo 'hello'"
    @$(if $(DOCKER_SERVICE_NAME),,$(error DOCKER_SERVICE_NAME is undefined))
    @$(if $(COMMAND),,$(error COMMAND is undefined))
    $(EXECUTE_IN_CONTAINER) $(COMMAND);

.PHONY: enable-xdebug
enable-xdebug: ## Enable xdebug in the given container specified by "DOCKER_SERVICE_NAME". E.g. "make enable-xdebug DOCKER_SERVICE_NAME=php-fpm"
    "$(MAKE)" execute-in-container APP_USER_NAME="root" DOCKER_SERVICE_NAME=$(DOCKER_SERVICE_NAME) COMMAND="sed -i 's/.*zend_extension=xdebug/zend_extension=xdebug/' '/etc/php8/conf.d/zz-app-local.ini'"

.PHONY: disable-xdebug
disable-xdebug: ## Disable xdebug in the given container specified by "DOCKER_SERVICE_NAME". E.g. "make enable-xdebug DOCKER_SERVICE_NAME=php-fpm"
    "$(MAKE)" execute-in-container APP_USER_NAME="root" DOCKER_SERVICE_NAME=$(DOCKER_SERVICE_NAME) COMMAND="sed -i 's/.*zend_extension=xdebug/;zend_extension=xdebug/' '/etc/php8/conf.d/zz-app-local.ini'"
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы перехватывать входящие запросы, нам нужно заставить PhpStorm прослушивать соединения PHP Debug через Run | Start Listening for PHP Debug Connections.

Соответствующие порты настраиваются в File | Settings | PHP | Debug. В Xdebug < 3 порт по умолчанию был 9000, а в Xdebug 3 это 9003.

Наконец, нам нужно добавить сервер через File | Settings | PHP | Servers.

Имя сервера должно соответствовать значению ключа serverName в переменной окружения PHP_IDE_CONFIG, которую мы настроили ранее как serverName=dofroscra.

php-fpm

Для php-fpm мы должны перезапустить процесс php-fpm без перезапуска контейнера после активации xdebug через

kill -USR2 1
Войти в полноэкранный режим Выйти из полноэкранного режима

Поскольку это трудно запомнить, мы добавляем цель make в .make/01-01-application-commands.mk.

# @see https://stackoverflow.com/a/43076457
.PHONY: restart-php-fpm
restart-php-fpm: ## Restart the php-fpm service
    "$(MAKE)" execute-in-container DOCKER_SERVICE_NAME=$(DOCKER_SERVICE_NAME_PHP_FPM) COMMAND="kill -USR2 1"
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь мы можем просто запустить

make enable-xdebug DOCKER_SERVICE_NAME=php-fpm
make restart-php-fpm
Войти в полноэкранный режим Выйти из полноэкранного режима

Установка точки останова в public/index.php и открытие http://127.0.0.1/ в браузере или через curl http://127.0.0.1/ остановит выполнение, как и ожидалось.

cli

Вместо того, чтобы запускать PHP-скрипт через HTTP-запрос, мы также можем запускать CLI-скрипты – вспомните, например, цель make setup-db. Чтобы отладить такие вызовы, нам нужно выполнить те же шаги, что и раньше:

  • включить расширение xdebug в контейнере application.
  • “Прослушивание подключений для отладки PHP” из PhpStorm

Выполнение следующих целей make вызовет точку останова в setup.php:

make enable-xdebug DOCKER_SERVICE_NAME=application
make setup-db
Войти в полноэкранный режим Выход из полноэкранного режима

php-workers

И, наконец, то же самое для долго работающих процессов PHP (они же рабочие). Как и раньше:

  • включите расширение xdebug в контейнере php-worker.
  • “Прослушивание соединений для отладки PHP” из PhpStorm
  • перезапустить php workers

Выполнение следующих целей make вызовет точку останова в worker.php:

make enable-xdebug DOCKER_SERVICE_NAME=php-worker
make restart-workers
Войти в полноэкранный режим Выйти из полноэкранного режима

strace

strace – это отличный инструмент для отладки долго работающих процессов, который я взял на вооружение после прочтения книги “Что делает PHP?”. Я добавил его в базовый образ php:

RUN apk add --update --no-cache 
        strace
Войти в полноэкранный режим Выход из полноэкранного режима

Вы можете подключиться к любому запущенному процессу через sudo strace -p $processId – НО это не работает из коробки на docker и приведет к ошибке с сообщением об ошибке

strace: attach: ptrace(PTRACE_SEIZE, 1): Operation not permitted
Войти в полноэкранный режим Выйти из полноэкранного режима

Это вызвано мерой безопасности docker и может быть обойдено путем добавления

services:
  service:
    cap_add:
      - "SYS_PTRACE"
    security_opt:
      - "seccomp=unconfined"
Ввести полноэкранный режим Выйти из полноэкранного режима

в .docker/docker-compose/docker-compose.local.yml ко всем контейнерам PHP. После восстановления и перезапуска установки docker теперь можно, например, войти в контейнер php-worker и запустить strace на рабочем процессе php:

application:/var/www/app# ps aux
PID   USER     TIME  COMMAND
    1 applicat  0:00 {supervisord} /usr/bin/python3 /usr/bin/supervisord
    7 applicat  0:00 php /var/www/app/worker.php
    8 applicat  0:00 php /var/www/app/worker.php
    9 applicat  0:00 php /var/www/app/worker.php
   10 applicat  0:00 php /var/www/app/worker.php
   11 applicat  0:00 bash
   20 applicat  0:00 ps aux
application:/var/www/app# sudo strace -p 7
strace: Process 7 attached
restart_syscall(<... resuming interrupted read ...>) = 0
poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
sendto(4, "*2rn$4rnRPOPrn$5rnqueuern", 25, MSG_DONTWAIT, NULL, 0) = 25
poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=4, revents=POLLIN}])
recvfrom(4, "$", 1, MSG_PEEK, NULL, NULL) = 1
Войти в полноэкранный режим Выход из полноэкранного режима

Подведение итогов

Поздравляем, у вас все получилось! Если что-то не совсем понятно, не стесняйтесь оставить комментарий. Кроме того, теперь у вас должна быть полностью настроенная система разработки, работающая с PhpStorm в качестве IDE.

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

Пожалуйста, подпишитесь на RSS канал или по электронной почте, чтобы получать автоматические уведомления о выходе следующей части 🙂

Оцените статью
Procodings.ru
Добавить комментарий