Xdebug 配置¥传说高级PHP

PHP技术
430
0
0
2022-09-24

Homestead 让我知道了环境的痛 xdebug 让我知道了php的痛

所以有钱人还是买苹果好一些,php fpm下原生才好玩,扩展有时装到爆炸! 主要机器,通讯,权限,版本兼容问题!且行且珍惜!生命可贵,有bug就去散散步 回来就好了,哈哈哈哈哈哈

Xdebug

xdebug 一个PHP扩展对原生php的打印函数进行了封装,服气器配置,和友好的IDE 配合,会有很好的debug体验!

原理

  • 客户端IDE 和 PHPserver 基于通讯协议BDGP DEBUG
  • 需配置基于特定端口9000 cookie:PHPSTORM 识别通讯

一键安装

  • homestead: xon 启用xdebug xoff 关闭xoff
  • ubuntu: sudo apt-get install php7.4-xdebug
  • cli: 执行php -v 或者 php -m 查看是否安装好xdebug
  • 安装似乎特别简单,但要注意版本问题,否则不能debug xdebug.org/docs/compat#supported-v...
  • 终极版本确认: xdebug.org/wizard
  • <?php echo phpinf0(); ?>的信息 查看源码的方式 全部复制黏贴到 白色框内,
  • 点击Analyse my phpinfo() output,即可查看相应的 xdebug版本
  • php.ini Xdebug2.配置 Xdebug3. 参数已做调整 需要修改 否则报错 renamed
zend_extension = php_xdebug.dll
xdebug.profiler_enable = On
xdebug.profiler_enable_trigger = On
xdebug.profiler_output_name = cachegrind.out.%t.%p
xdebug.profiler_output_dir ="T:\laragon\xdebug"
xdebug.show_local_vars=0
xdebug.idekey=PHPSTORM
xdebug.remote_enable = On
xdebug.remote_host=localhost
xdebug.remote_port=9000
xdebug.remote_handler=dbgp

PHPstorm

php 版本要与xdebug 版本匹配 phpstorm 最好也要对应时间版本 过久过新 也可能不适配 !

进入Settings>PHP>Debug>DBGp Proxy 配置idekey 本地域名 debug端口9000

进入Settings>PHP>Servers phpcode 对应服务器 通常 测试域名 对应端口80

进入Run > Edit configguration 配好需要请求的域名

扩展:

命令行debug: xdebug.org/docs/dbgpClient

本地配置简单,但php xdebug phpstorm 三者之间版本一定要适配,本人就是坑在这里!泪崩!

关于 PhpStorm 2020.3 和 Xdebug 3

在前文中,笔者用的还是 PhpStorm 2020.2.4,所以结合 Xdebug 3.0.0 在使用 Web Server Debug Validation 进行 调试环境验证 时,提示类似 Xdebug port is invalid 的报错,分析是 不兼容 3.0.0 版本 导致的。所以降级成了 Xdebug 2.9.8 才顺利写完了文章。

写完前文的第二天,PhpStorm 2020.3就发布了,新增

  • 对 PHP 8 的支持
  • 对 Xdebug 3 的支持

在重新用 PHPBrew 安装 PHP 8 + Xdebug 3.0.0 环境并且用 Nginx + php-fpm 部署起来后,Web Server Debug Validation 成功验证。

关于 Web Server Debug Validation 功能

笔者在前文写完后,在升级 PhpStorm 2020.3 之前,笔者重新安装了 PHP 7.4.13 + Xdebug 3.0.0 环境,在忽略 验证报错 的情况下直接开启调试,是完全没有任何问题的。所以,该功能只是 验证调试环境,在调试之前仅作参考,不影响实际调试。

关于 远程调试

笔者在写前文关于 远程调试 的几节时,参考了网上很多文章,看似吃透了,其实不然,原因有这样几点:

IDE 结合 Xdebug 调试的原理

  • IDE(本身或利用插件) 打开本地的 9000 端口并进行监听(Xdebug 2.X 默认为 9000,Xdebug 3.X 默认为 9003,但均可修改)
  • IDE(本身或利用插件) 做好路径映射(path mapping),即本地在IDE中打开的项目目录与远程服务器上的项目目录做一一映射,例如
  • macOS 本地:/Users/chaos/Work/php/demos/debug/
  • Linux 服务器:/home/chaos/Work/php/demos/debug/
  • 本地向服务器发送请求时带上 Cookie: XDEBUG_SESSION=IDEKEY
  • 服务器接受到请求时,经历了 Nginx -> php-fpm 后到达 XdebugXdebug 检测到 XDEBUG_SESSION 的 cookie,认为这条请求是带着 调试目的 来的,同时挂起 PHP解释器 进一步处理请求
  • 然后 Xdebug 从 php.ini 中获取目标地址或从 $_SERVER 里获取到请求的来源地址(比如 223.104.148.182)作为目标地址,然后就向目标地址的 9000 端口发起建立 调试连接
  • 本地的 IDE(本身或利用插件) 发现监听的 9000 端口有 调试连接 建立,判断一下 XDEBUG_SESSION 是否为自己预设的 IDE key
  • 如果不是预设 IDE key,通过 9000 端口上的 调试连接 告诉服务器的 Xdebug,“不归我管,我不处理”,然后双方协商一下断连接
  • 如果是预设 IDE key,同时发现本地打了断点或者本地设置了 "stopOnEntry": true (VSCode) / Break at first line in PHP scripts (PhpStorm),则告诉服务器的 Xdebug,“收到,调试准备就绪”,然后双方协商一下进入调试状态

真实环境下 远程调试 的 陷阱

聪明的小伙伴可能已经发现上述 调试流程 里存在的一个 陷阱:即服务器要向本地请求建立 调试连接,但问题是,现在所有的 家用/企业 网络环境下,所谓的 本地 都在上级路由器的 NAT 下,根本就没有暴露在公网的 IP地址,所以 本地 的 9000 端口对于服务器来说,是不可达的,想要访问,做梦!

填坑 陷阱

笔者重新模拟了真实的调试环境,即 本地 为 macOS远程服务器 选了两台 Linux,一台是笔者在公司搭建的 物理机,另一台是公司购买的阿里云 云主机

填坑的最终目的,是要使得 本地 的 9000 端口暴露给服务器,让其直接可达。

这里分:

  1. 本地和远程服务器同处一个局域网内,例如,都加入同一个 VPN 网络,本地通过 VPN 分配给服务器的 私网IP 访问服务器,服务器的 Xdebug 解析到的来源地址则也是通过 VPN 分配给本地的 私网IP,直接可达。
  2. 路由器本身从 ISP(宽带运营商) 通过 PPPoE 拨号 获取到了 公网IP,然后路由器上通过 端口映射 或 DMZ 模式,将本地的 9000 端口,映射到路由器的 9000 端口,这样服务器也可通过 公网IP:9000 访问到本地的 9000 端口。(该方式最推荐,但是在国内可行度不高,因为国内 IP地址池 即将枯竭,所以很难从运营商处申请到 公网IP
  3. 其他环境只能借助 移花接木大法:借助 SSH 反向隧道,在本地和服务器之间建立一条 TCP通道,将本地的 9000 端口映射到服务器的 9000 端口。这样的话,服务器上的 Xdebug 访问 localhost:9000 就等于访问到了 IDE本地 的 9000 端口。(借用 JetBrains 官方文档里的一副插图)
  4. JetBrains 官方文档关于 SSH 隧道的插图
  5. JetBrains 官方文档关于 SSH 隧道的插图

在这里,笔者将前两种环境归纳为 回程网络直接可达,否则则为 回程网络不可直达

PhpStorm 的特殊配置

在分别罗列远程调试的具体参数配置之前,笔者还得额外将 PhpStorm 的特殊配置单独拎出来阐述一下。

PhpStorm 调试的 目标服务器,以 Server 的存在进行配置,具体位于 偏好设置 的 Languages and Frameworks | PHP | Servers 下。详见 JetBrains 官方文档,原文是这样的:

On this page, configure HTTP access for debugging engines to interact with local and remote Web servers and set correspondence between files on the server and their local copies in the PhpStorm project.
在本页,为调试配置 HTTP 访问,用以在 本地 和 远程 Web 服务器 之间交互,以及为 远程服务器的文件 与其在 PhpStorm 工程 中打开的 本地拷贝 设置关联。

PhpStorm 调试的特殊配置:在远程服务器上的 php-fpm 配置文件里添加这两行:

clear_env = no
env["PHP_IDE_CONFIG"] = "serverName=UbuntuServer"

这里 UbuntuServer 是自定义的服务器名称,自行更改。

然后在 PhpStorm 偏好设置的 Languages and Frameworks | PHP | Servers 添加一个 Server

  • name 填 UbuntuServer
  • Host 和 Port 笔者亲测可填任意合法值,PhpStorm 不校验,所以笔者都填写了 0 值
  • Debugger 选择 Xdebug
  • 勾选上 Use path mappings,并且设置好 本地文件目录路径 和 服务器文件目录路径 的映射(比如笔者本地的 /Users/chaos/Work/php/demos/debug/ 与服务器的 /home/chaos/Work/php/demos/debug/

PhpStorm 里配置 Server

PhpStorm 里配置 Server

VSCode 的配置

只需要比本地调试多配置一个 路径映射,即 pathMappings 键值对,附上 launch.json 文件内容:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for XDebug",
      "type": "php",
      "request": "launch",
      "port": 9000,
      // "stopOnEntry": true,
      "pathMappings": {
        "/home/chaos/Work/php/demos/debug/": "${workspaceRoot}/",
      }
    },
    {
      "name": "Launch currently open script",
      "type": "php",
      "request": "launch",
      "program": "${file}",
      "cwd": "${fileDirname}",
      "port": 9000
    }
  ]
}

这里的 "/home/chaos/Work/php/demos/debug/": "${workspaceRoot}/" 是 "/home/chaos/Work/php/demos/debug/": "/Users/chaos/Work/php/demos/debug/" 的简化形式,${workspaceRoot} 则为 VSCode 中打开的 项目根目录

服务器的 php.ini 在不同情况下的配置

情况一:回程网络直接可达

  • Xdebug 2.X

[xdebug]
xdebug.remote_enable=1
xdebug.remote_connect_back=1
remote_enable=1 表示开启远程调试
remote_connect_back=1 表示获取请求发起地址(从 $_SERVER['HTTP_X_FORWARDED_FOR'] 和 $_SERVER['REMOTE_ADDR'] 中获取),反向访问发起地址的 9000 端口建立调试连接
  • Xdebug 3.X

[xdebug]
xdebug.mode=debug
xdebug.discover_client_host=true
xdebug.port=9000
mode=debug 取代了 2.X 版本的 remote_enable=1
discover_client_host=true 取代了 2.X 版本的 xdebug.remote_connect_back=1
port=9000 是个人喜好,在 3.X 中默认为 9003。笔者倾向于保持与 2.X 一致,这样同一份 IDE 配置可以同时调试 2.X 版本和 3.X 版本

情况二:回程网络不可直达

先用 SSH 隧道 反向将本地的 9000 端口映射到服务器的 9000 端口上

ssh -g -N -R 9000:127.0.0.1:9000 user@server
  • Xdebug 2.X (Method A)
[xdebug]
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
remote_host=127.0.0.1 表示直接向 127.0.0.1 这个地址请求建立调试连接
  • Xdebug 2.X (Method B)

[xdebug]
xdebug.remote_enable=1
xdebug.remote_connect_back=1
xdebug.remote_addr_header="HTTP_X_XDEBUG_REMOTE_ADDR"
remote_connect_back=1 表示获取请求发起地址,反向访问发起地址的 9000 端口建立调试连接
xdebug.remote_addr_header="HTTP_X_XDEBUG_REMOTE_ADDR" 表示优先从 $_SERVER[‘HTTP_X_XDEBUG_REMOTE_ADDR’] 获取发起地址,获取不到再去 $_SERVER['HTTP_X_FORWARDED_FOR'] 和 $_SERVER['REMOTE_ADDR'] 中寻找
| 调试请求类似于 $ curl server.com:8000 -b XDEBUG_SESSION=IDEKEY -H "X-Xdebug-Remote-Addr: 127.0.0.1"
| 注意:HTTP- 发送请求时不用添加,因为对于未自定义的 headerNginx 会自动加上前缀 HTTP_,并全部大写,横线 转为 下划线,存入 $_SERVER 全局变量中
  • Xdebug 3.X (Method A)

[xdebug]
xdebug.mode=debug
xdebug.port=9000
xdebug.client_host=127.0.0.1
client_host=127.0.0.1 表示直接告诉 Xdebug,发起地址就是 127.0.0.1,你往这个地址请求建立调试连接就可以了
  • Xdebug 3.X (Method B)

[xdebug]
xdebug.mode=debug
xdebug.port=9000
xdebug.discover_client_host=true
xdebug.client_discovery_header="HTTP_X_XDEBUG_REMOTE_ADDR"
client_discovery_header="HTTP_X_XDEBUG_REMOTE_ADDR" 取代了 2.X 版本的 xdebug.remote_addr_header="HTTP_X_XDEBUG_REMOTE_ADDR"

其中 Xdebug 2.X (Method B) 与 Xdebug 3.X (Method B) 还有些许不同:

  • Xdebug 2.X (Method B) 如果按配置规则没找到请求发起地址,它不会降级使用 127.0.0.1/localhost 作为请求发起地址
  • Xdebug 3.X (Method B) 如果按配置规则没找到请求发起地址,它会降级使用 127.0.0.1/localhost 作为请求发起地址

多人调试 - DBGp

先附上参考文档

这块我不仅会略过,我还会狠狠的吐槽一下。先来看一下上述 “Multiuser debugging via Xdebug proxies” 这篇文里的一张插图

PhpStorm 提供的 DBGp 原理图

PhpStorm 提供的 DBGp 原理图

看似很美妙是不是,在笔者下载了 dbgpProxy 并且反复实验后,发现这个工具真的是神坑。

笔者在那台阿里云上运行 ./dbgpProxy -i 0.0.0.0:9001 -s 127.0.0.1:9000 后,从本地的 PhpStorm 带着自定义IDE key "PS" 发起调试请求,结果 dbgpProxy 日志打印 Connecting to 112.3.2.42:9000,而笔者所在的本地宽带并没有公网IP,112.3.2.42 这个IP是多层 NAT 之前的IP,肯定访问不进来,只能上 SSH 隧道方案

阿里云运行 dbgpProxy

那这个 dbgpProxy 的意义何在呢?笔者在网上找到了 Xdebug 的作者 Derick Rethans(他同时设计了 DBGp 协议)的一篇文章 - Debugging with multiple users,在文中,他描绘了 DBDp 的使用场景:

Running a DBGp proxy also allows you to avoid NAT issues where (as seen from PHP+Xdebug on the server) all connections seem to come from the same IP (because your internal network is NATted). In this case, you can simple run the dbgp proxy on your NAT machine, configure xdebug.remote_host setting to the IP address of your NAT machine, and configure the IDEs to connect to the proxy running at :9001.

大致解释一下:

  • 你的路由器得从ISP处获取到公网IP
  • 在你的路由器上运行 dbgpProxy
  • 路由器下所有人的的 IDE 的 DBGp Proxy 配置 的 Host 填写路由器网关,即路由器内网地址

到这里,你可以发现,搭建 DBGp调试环境 非常苛刻,结合 公网IP 和 路由器运行 dbgpProxy 这两点看,只有 软路由 可以满足,因为 dbgpProxy 的二进制可执行文件只有 Windows / macOS / Linux x86-64 版本。所以大部分人/公司 可以洗洗睡了。

结语

为了写这两篇文,笔者是翻来覆去做了很多实验,还整理了很多知识点,打了很多草稿(真正意义上的纸质草稿),最后附上最满意的一张草稿。

笔者最满意的一张草稿

笔者最满意的一张草稿

希望读者们能有所收获,感谢阅读!