在 PHP 8.1 上使用 PhpStorm、Docker 和 Xdebug 3

调试 PhpStorm 中所有内容的指南(IDE、浏览器 [fpm]、cli 和 worker)

在本教程中,我们将设置本地 dockerized PHP 开发环境以供 PhpStorm 和 Xdebug 使用。我们还将确保我们可以从命令行以及从 PhpStorm 运行 PHPUnit 测试。还有一个 Youtube 视频来指导您完成整个过程

https://www.youtube.com/watch?v=bZ1MiynqT98

所有代码示例都在我在 github 上的 Docker PHP 教程存储库中公开可用。您可以在part-4-2-phpstorm-docker-xdebug-3-php-8-1-in-2022找到本教程的分支。本教程之前发表在我的个人博客PhpStorm, Docker and Xdebug 3 on PHP 8.1 in 2022

介绍

本文是使用 Xdebug 设置 PhpStorm 以在 Docker 上进行本地开发的更新,但还将涵盖调试 php-fpmphp 工作进程的“剩余案例” 。

我们仍将依赖于通过 SSH 配置连接到的始终运行的 docker 设置,而不是使用内置的 docker-compose 功能,因为我觉得它更接近我们在 CI / 生产中所做的。但是,我们将不再使用 SSH 密钥,而是简单地通过密码进行身份验证。

这降低了复杂性并消除了有关“SSH 密钥在存储库中公开”的任何令人讨厌的警告。

安装工具

安装作曲家

通过拉取官方的 composer docker 镜像并简单地将 composer 可执行文件“复制”到基础 php 镜像来安装Composer 。此外,作曲家需要扩展mbstringphar

# 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_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 < 3 中调用),请参阅从 Docker 容器运行时在 PhpStorm 上修复 Xdebug 。这仍然适用于 Docker Desktop,但对于 Linux 用户,我们需要将魔术引用添加到所有 PHP 容器(我们不能将其添加到 php 基础映像,因为这是运行时设置):xdebug.client_host=host.docker.internalxdebug.remote_hosthost-gateway

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 在容器中运行。为了使这更方便,在以下位置添加了用于运行任意 Composer 命令的 make 目标.make/01-00-application-setup.mk

.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将使用application容器的 PHP 版本和扩展来安装依赖项,但我仍然会在本地看到安装的文件,因为代码库被配置为容器的卷。

在安装 phpunit 之前,我们必须添加所需的扩展domxml容器

# 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

现在我们可以通过

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
...

我还添加了

  • 一个最小的phpunit.xml配置文件
  • 一个测试用例tests/SomeTest.php
  • 以及“与 qa 相关的任何内容”的新 Makefile .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

application我们将通过容器中的 ssh 从 PhpStorm 执行命令。如前所述,我们不会使用密钥文件进行身份验证,而是简单地使用通过APP_SSH_PASSWORD变量 in配置.docker/.env并传递给图像 in的密码.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 配置来代替,并在Cli 解释器界面中配置路径映射

SSH 配置

File | Settings | Tools | SSH Configurations使用以下设置创建一个名为“Docker PHP 教程”的新 SSH 配置

  • 主机: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使用新 SSH 配置的新 PHP CLI 解释器

此外,我们定义了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

当从 Docker 容器运行时,我们仍然需要修复 PhpStorm 上的 Xdebug,方法是为xdebug.client_host=host.docker.internal. 这与我们在 中使用的值相同.docker/images/php/base/conf.d/zz-app-local.ini

在解释器概述中,我们现在必须配置路径映射,以便 PhpStorm 知道“哪个本地文件属于哪个远程文件”。远程文件夹在.docker/.envvia中定义

APP_CODE_PATH_CONTAINER=/var/www/app

之后我们可以设置断点,例如 insetup.php并开始调试:

屏幕截图显示 PhpStorm 添加了我们之前定义的 Xdebug 扩展。

PHPUnit

phpunit通过配置File | Settings | PHP | Test Frameworks。首先,我们选择刚刚添加的解释器

然后,我们将路径添加到 composer 自动加载脚本和phpunit.xml配置文件。

applicationPhpStorm 现在将使用容器中的 PHP 解释器执行测试

调试

首先,如果您还没有,请查看官方 xdebug 文档。Derick 在详细解释 xdebug 方面做得很好,包括一些有用的视频,例如Xdebug 3: Xdebug with Docker and PhpStorm in 5 minutes

通过 PhpStorm 执行的调试代码

这应该已经可以开箱即用了。只需设置一个断点,右键单击一个文件并选择“调试’…’”

通过 php-fpm、cli 或从 worker 执行的调试代码

对于没有 PhpStorm 的容器“直接”执行的代码,我们首先需要xdebug在容器中通过移除;扩展名前面的/etc/php8/conf.d/zz-app-local.ini

; Note:
; Remove the comment ; to enable debugging
zend_extension=xdebug

为了让这更方便一点,我们使用专门的 make recipe 来处理这些操作.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 通过Run | Start Listening for PHP Debug Connections.

对应的端口配置在File | Settings | PHP | Debug。在 Xdebug < 3 中,默认端口是9000,而在Xdebug 3 中,默认端口是9003

最后,我们需要通过添加服务器File | Settings | PHP | Servers

服务器的名称必须与我们之前配置为serverName的环境变量中的键值匹配。PHP_IDE_CONFIGserverName=dofroscra

php-fpm

因为php-fpm我们必须在通过以下方式激活后重新启动php-fpm进程而不重新启动容器xdebug

kill -USR2 1

由于这很难记住,我们在.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

除了通过 HTTP 请求触发 PHP 脚本,我们还可以运行 CLI 脚本 –make setup-db例如考虑目标。要调试此类调用,我们需要遵循与之前相同的步骤:

  • 在容器中启用xdebug扩展application
  • 来自 PhpStorm 的“侦听 PHP 调试连接”

运行以下 make 目标将在 中触发断点setup.php

make enable-xdebug DOCKER_SERVICE_NAME=application
make setup-db
php-workers

最后,对于长时间运行的 PHP 进程(又名工作人员)也是如此。和以前一样:

  • 在容器中启用xdebug扩展php-worker
  • 来自 PhpStorm 的“侦听 PHP 调试连接”
  • 重新启动 php 工作人员

运行以下 make 目标将在 中触发断点worker.php

make enable-xdebug DOCKER_SERVICE_NAME=php-worker
make restart-workers

包起来

恭喜,你成功了!如果现在有些事情还不完全清楚,请不要犹豫发表评论。除此之外,您现在应该拥有一个完全配置的开发设置,可以使用 PhpStorm 作为您的 IDE。

如果您喜欢此类内容,请随意:

using-phpstorm-docker-and-xdebug-3-on-php-81