如果你认为 Xdebug 有用,请考虑支持该项目

单步调试

Xdebug 为调试客户端提供了一个与运行的 PHP 脚本交互的接口。这个章节说明了如何设置 PHP 和 Xdebug 来实现它,并介绍了一些客户端。

介绍

Xdebug (远程)调试器允许你检查数据结构,交互地遍历和调试代码。使用的协议是公开的,叫做 DBGp。这个协议是在 Xdebug 2 中实现的,它取代了旧版本的不再被支持的 GBD 协议。

客户端

这里有很多客户端实现(免费的和商业的)。他们都是第三方工具,所以请参考原作者的支持

用于调试的简单命令行客户端 dbgpClient下载页面提供。

开启调试器

为了启用 Xdebug 调试器,需要在 php.ini 中做一些配置设置。这些设置是 xdebug.remote_enable 用来开启调试器,xdebug.remote_hostxdebug.remote_port 用来配置调试器需要连接的 IP 地址和端口。如果开发中的服务器是多个开发者共享的,这里也有一个 xdebug.remote_connect_back 设置可以使用。

当错误状况出现时(PHP 错误或者异常),如果想调试器初始化一个会话,那么需要更改 xdebug.remote_mode 设置。这个设置允许的值是 "req"(默认),只要当一个脚本启动它就能让调试器初始化一个会话,或者是 "jit",当有错误时会话才能被初始化。

在这些设置之后,当脚本运行 Xdebug 仍然不能自动的开启一个调试会话。你需要激活 Xdebug 调试器,有这三种方法可做:

使用一个环境变量

从命令行运行脚本,需要设置一个环境变量,如:

export XDEBUG_CONFIG="idekey=session_name"
php myscript.php

在 Windows 上,使用 set 设置一个环境变量:

set XDEBUG_CONFIG="idekey=session_name"

也可以在这个环境变量里配置 xdebug.remote_host, xdebug.remote_portxdebug.remote_modexdebug.remote_handler ,只要用空格分隔其值:

export XDEBUG_CONFIG="idekey=session_name remote_host=localhost profiler_enable=1"

所有设置可以通过 XDEBUG_CONFIG 设置,也可以用常规的 php.ini 设置。

使用一个 GET 参数

如果想通过一个网页浏览器来启动调试一个脚本,只需要添加 XDEBUG_SESSION_START=session_name 为参数到 URL 中。若不使用 GET 参数,也可以通过一个 POST 参数或者通过一个名为 XDEBUG_SESSION 的 cookie 来设置 XDEBUG_SESSION_START。参考下一节来了解从一个浏览器窗口调试会话如何工作。

使用一个浏览器扩展

当运行 PHP 时,另一种通过 web 服务器激活调试器的方法是安装以下四个浏览器扩展中的一个。他们中的每一个都允许你简单的通过点击一个按钮来开启调试器。当这些扩展能有效工作时,他们可以直接设置 XDEBUG_SESSION cookie,而不是如接下来的 HTTP 调试会话 描述的通过 XDEBUG_SESSION_START 设置。

这些扩展是:

Xdebug Helper for Firefox
Firefox 的这个扩展的建立是为了在 IDE 中让调试更加容易。可以在这里找到这个扩展 GitHub
Xdebug Helper for Chrome
Chrome 的这个扩展会用一个单击让你启用/禁用调试和效能分析。可以在这里找到这个扩展 https://chrome.google.com/extensions/detail/eadndfjplgieldjbigjakmdgkmoaaaoc
XDebugToggle for Safari
Safari 的这个扩展会允许你在 Safari 中自动开启 Xdebug 调试。可以从 Github 中得到它 https://github.com/kampfq/SafariXDebugToggle
Xdebug launcher for Opera
Opera 这个扩展允许从 Opera 中开启一个 Xdebug 会话。

在开启脚本之前需要告诉客户端它可以接收调试连接了,请参考指定客户端如何操作的文档。在下载之后为了使用绑定的客户端简单的开启它,可以通过在 Linux 运行 dbgpClient,MacOS 上运行 dbgpClient-macos,Windows 上运行 dbgpClient.exe

当调试客户端开启,它会显示版本信息,然后等待连接被调试服务器(Xdebug)初始化:

Xdebug Simple DBGp client (0.2)
Copyright 2019-2020 by Derick Rethans
In dumb client mode

Waiting for debug server to connect on port 9000.

建立连接后,调试服务器的输出显示为:

Connect from [::1]:53764
DBGp/1.0: Xdebug 3.0.0-dev — For PHP 7.4.3-dev
Debugging file:///tmp/test-async.php (ID: 13699/derick)
(cmd)  

现在可以如 DBGp 文档页面所说明的一样使用指令集。当脚本结束,调试服务器断开了来自客户端的连接,调试客户端会继续等待下一个新的连接。关于命令行调试客户端这里还有更全面的文档。

通信设置

一个静态 IP/单个开发者

在远程调试中,Xdebug 内嵌于 PHP 中表现得像客户端和作为服务器的 IDE。下面的动画展示了通信信道是如何设置的:

一个未知 IP/多个开发者

如果 zdebug.remote_connect_back 被使用,设置会略有不同:

HTTP 调试会话

Xdebug 包含跟踪一个通过浏览器 cookie 开启的调试会话的功能。这像这样工作:

多用户调试

当远程调试时,Xdebug 只允许指定一个 IP 地址连接到 xdebug.remote_host。它不会自动连接到浏览器运行的本机 IP 地址,除非使用 xdebug.remote_connect_back

如果所有开发者不同的项目工作在同一台(开发阶段)服务器上,可以通过 Apache 的 .htaccess 功能使用 php_value xdebug.remote_host=10.0.0.5 设置 xdebug.remote_host 每个目录。然而多个开发者使用同一处的代码这种情况,.htaccess 这一招将不会生效,因为这里的代码的目录是一样的。

这个问题有两种解决方案。首先,可以使用 DBGp 代理。概述如何使用这个代理,请参考多用户调试的文章。你可以下载 ActiveState 网站上的代理服务器作为 python 远程调试包的一部分。这里有更多的文档 Komodo FAQ

其次你可以使用由 Xdebug 2.1 引入的 xdebug.remote_connect_back 设置

实施细则

Xdebug DBGp 协议的 context_name 的命令实现不取决于堆栈层级。在每个调试器会话中返回值总是相同的,因此可以被安全的缓存。

定制 DBGp 命令

DBGp 协议允许调试器引擎指定前缀为 xcmd_ 的特定命令。Xdebug 包括其中的一些,这里记录了他们。

DBGp: xcmd_profiler_name_get

如果 Xdebug 的 profiler 当前处于活跃状态(查看 Profiling PHP 脚本),这个命令返回正在被写入 profiling 信息的文件名。

DBGp: xcmd_get_executable_lines

这个命令返回,在活跃的堆栈框架中,哪些行可以具有工作断点。这些是在它们上面有 EXT_STMT 操作码的行。这个命令接受 -d 选项,该选项显示堆栈深度,为 0 表示最高级堆栈帧。

这个命令返回如下 XML 格式的信息:

<?xml version="1.0" encoding="iso-8859-1"?>
<response
  xmlns="urn:debugger_protocol_v1"
  xmlns:xdebug="https://xdebug.org/dbgp/xdebug"
  command="xcmd_get_executable_lines"
  transaction_id="10">
	<xdebug:lines>
		<xdebug:line lineno="2"></xdebug:line>
		<xdebug:line lineno="3"></xdebug:line>
		<xdebug:line lineno="4"></xdebug:line>
		<xdebug:line lineno="6"></xdebug:line>
		<xdebug:line lineno="8"></xdebug:line>
	</xdebug:lines>
</response>

相关设置


integer xdebug.extended_info = 1

可用于 Xdebug < 2.8

控制是否让 Xdebug 强制 PHP 解析器开启 "extended_info" 模式,它允许 Xdebug 在远程调试器中执行文件或行断点。当跟踪或者 profiling 脚本时通常应该关闭这个选项,因为 PHP 生成的 oparray 将会增加大约三分之一的大小从而让脚本变慢。这个设置不能在脚本中用 ini_set() 设置,只能在 php.ini 中设置。


string xdebug.idekey = *complex*

控制哪一个 IDE 键应该传递到 DBGp 调试处理器上,默认是基于环境设置的。首先是考虑环境设置 DBGP_IDEKEY,然后是 USER,最后是 USERNAME。默认设置为找到的第一个环境变量。如果找不到则设置默认为''。如果这个设置设置了,它将总是会覆盖环境变量。


string xdebug.remote_addr_header = ""

Xdebug >= 2.4 引入

如果 xdebug.remote_addr_header 配置为非空字符串,那么该值将用作超全局数组 $SERVER 中的键,来确定哪个头部用来查找用于连接的 IP 地址或主机名。这个设置只能与 xdebug.remote_connect_back 结合使用,否则将被忽略。


boolean xdebug.remote_autostart = false

通常来说你需要使用一个指定的 HTTP GET/POST 变量来开启远程调试(查看远程调试)。当这个设置设为 1 时,即便 HTTP GET/POST 变量不存在,Xdebug 也将总是尝试开启一个远程调试会话并且尝试连接到一个客户端。


boolean xdebug.remote_connect_back = false

Xdebug >= 2.1 引入

如果开启,xdebug.remote_host 设置将会被忽略,并且 Xdebug 将会让 HTTP 请求尝试连接到客户端。它会检查 $_SERVER['REMOTE_ADDR'] 变量来寻找哪个 IP 地址可以使用。

如果配置了 xdebug.remote_addr_header ,那么将在 $_SERVER['HTTP_X_FORWARDED_FOR'] 和 $_SERVER['REMOTE_ADDR'] 变量之前检查具有配置名称的 $SERVER 变量。

这个设置不适用于通过 CLI 调试,因为 $SERVER 头部变量在这里不可用。

请注意这里没有过滤变量,即使它的地址不能匹配 xdebug.remote_host,任何人也都能连接到网络服务器然后开启一个调试会话。


integer xdebug.remote_cookie_expire_time = 3600

Xdebug >= 2.1 引入

这个设置可以通过会话 cookie 增加(或减少)远程调试会话保持活跃的时间。


boolean xdebug.remote_enable = false

这个开关控制 Xdebug 是否应该连接一个调试客户端来监听用 xdebug.remote_hostxdebug.remote_port 设置的主机和端口。如果一个连接不能被建立,脚本只将会像这个设置为 0 一样去继续运行。


string xdebug.remote_handler = dbgp

可用于 Xdebug < 2.9

只能是 'dbgp' 代表 调试器协议。DBGp 协议是唯一被支持的协议。


string xdebug.remote_host = localhost

选择运行调试客户端的主机,可以使用主机名、IP 地址或者 Unix 域套接字 'unix:///path/to/sock'。如果 xdebug.remote_connect_back 开启这个设置会被忽略。

对 Unix 域套接字支持由 Xdebug 2.6 引入。


string xdebug.remote_log =

配置一个文件名用来记录所有单步调试连接尝试、失败和通讯信息。

开启这个功能通过设置这个值为一个绝对路径。确保 PHP 运行的系统用户(比如 www-data,如果在 Apache 上运行)可以创建和写入这个文件。

这个文件是以 append-mode 打开,因此默认不能被重写。这里没有可用的并发保护。

当成功开启,日志文件将会包含 Xdebug 连接到 IDE 的任何尝试:

[1603325] Log opened at 2020-06-21 17:54:05
[1603325] I: Connecting to configured address/port: localhost:9000.
[1603325] W: Creating socket for 'localhost:9000', poll success, but error: Operation now in progress (29).
[1603325] I: Connected to client. :-)

它包含打开时间(2020-06-21 17:54:05),Xdebug 尝试连接的 IP/主机名和端口 (localhost:9000),以及是否成功 (Connected to client :-))。方括号([1603325])中的数字是进程 ID。

它也会记录自身的调试通讯信息,以 <init XML 元素开头:

<init
    xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug"
    fileuri="file:///home/httpd/www.xdebug.org/html/router.php"
    language="PHP" xdebug:language_version="7.4.2-dev"
    protocol_version="1.0" appid="1603325" idekey="XDEBUG_ECLIPSE">
        <engine version="2.9.6-dev"><![CDATA[Xdebug]]></engine>
        <author><![CDATA[Derick Rethans]]></author>
        <url><![CDATA[https://xdebug.org]]></url>
        <copyright><![CDATA[Copyright (c) 2002-2020 by Derick Rethans]]></copyright>
</init>

这个 fileuri 属性列出了应用的入口点,可以用于比较 breakpoint_set 命令路径映射是否正确设置。

超出 <init 元素,可以查找特性配置信息:

<- feature_set -i 4 -n extended_properties -v 1
-> <response
       xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug"
       command="feature_set" transaction_id="4" feature="extended_properties" success="1">
   </response>

以及连续命令

<- step_into -i 9
-> <response
       xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug"
       command="step_into" transaction_id="9"
       status="break" reason="ok">
           <xdebug:message filename="file:///home/httpd/www.xdebug.org/html/router.php" lineno="3">
           </xdebug:message>
   </response>

可以了解有关 DBGP - 一个通用调试协议规范 ,在它专用文档页面上。

这个 xdebug.remote_log_level 设置控制哪些信息被记录。

注意:现在很多 Linux 发行版使用 systemd,它实现了 private tmp 目录。这意味着 PHP 通过 web 服务器或者作为 PHP-FPM 运行,/tmp 目录会作为前缀类似于:

/tmp/systemd-private-ea3cfa882b4e478993e1994033fc5feb-apache.service-FfWZRg

integer xdebug.remote_log_level = 7

Xdebug >= 2.8引入

配置由单步调试发出的日志信息。

支持下列级别:

级别名称说明
1Errors连接错误
3Warnings连接警告
5Communication协议信息
7Information连接时的信息
10Debug断点解析信息

string xdebug.remote_mode = req

当一个调试连接初始化时选择。这个设置有两种不同的值:

req
只要脚本开启,Xdebug 将会尝试连接调试客户端。
jit
只有错误状况出现时,Xdebug 才会尝试连接调试客户端。

integer xdebug.remote_port = 9000

Xdebug 尝试连接远程主机的端口。Xdebug 和命令行调试客户端的默认端口都是9000。因为很多客户端使用这个端口数,最好保持这个设置不要更改。


integer xdebug.remote_timeout = 200

Xdebug >= 2.6引入

Xdebug 在 IDE 上等待确认传入调试连接以毫秒表示的时间量。默认值 200 毫秒在绝大多数情况下应该是足够的。如果经常遇到调试请求丢失,可能是因为有一个高延迟的网络,或者远离 IDE 的开发盒,或者防火墙速度慢,那么可以增加此值。

请注意,增加此值可能意味着 Xdebug 尝试建立连接请求看似“挂起”,但是 IDE 没有在监听。

相关函数


xdebug_break() : bool

发送一个断点到调试客户端

这个函数能让调试器在特定的行中断,就像一个普通文件/行断点被设置在该行一样。


xdebug_is_debugger_active() : bool

返回调试会话是否活跃

如果通过 DBGPS 的调试会话,与客户端连接当前处于活跃状态,则返回 true,否则,返回 false