1、安装 xdebug 扩展
本地环境windows
php -i
PHP Version => 7.2.31
System => Windows NT WINDOWS10-JACK 10.0 build 17134 (Windows 10) AMD64
Build Date => May 12 2020 10:22:06
Compiler => MSVC15 (Visual C++ 2017)
Architecture => x64
Configure Command => cscript /nologo configure.js "--enable-snapshot-build" "--enable-debug-pack" "--disable-zts" "--with-pdo-oci=c:\p
hp-snap-build\deps_aux\oracle\x64\instantclient_12_1\sdk,shared" "--with-oci8-12c=c:\php-snap-build\deps_aux\oracle\x64\instantclient_1
2_1\sdk,shared" "--enable-object-out-dir=../obj/" "--enable-com-dotnet=shared" "--without-analyzer" "--with-pgo"
Server API => Command Line Interface
Virtual Directory Support => disabled
Configuration File (php.ini) Path => C:\WINDOWS
Loaded Configuration File => D:\phpStudy\PHPTutorial\php\php-7.2.31-nts-Win32-VC15-x64\php.ini
Scan this dir for additional .ini files => (none)
Additional .ini files parsed => (none)
PHP API => 20170718
PHP Extension => 20170718
Zend Extension => 320170718
Zend Extension Build => API320170718,NTS,VC15
PHP Extension Build => API20170718,NTS,VC15
Debug Build => no
Thread Safety => disabled
Zend Signal Handling => disabled
Zend Memory Manager => enabled
Zend Multibyte Support => provided by mbstring
IPv6 Support => enabled
DTrace Support => disabled
Registered PHP Streams => php, file, glob, data, http, ftp, zip, compress.zlib, compress.bzip2, https, ftps, phar
Registered Stream Socket Transports => tcp, udp, ssl, tls, tlsv1.0, tlsv1.1, tlsv1.2
Registered Stream Filters => convert.iconv.*, string.rot13, string.toupper, string.tolower, string.strip_tags, convert.*, consumed, dec
hunk, zlib.*, bzip2.*
This program makes use of the Zend Scripting Language Engine:
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
将上面的内容粘贴到 xdebug wizard ,它会给出推荐的下载
下载之后将名字改成php_xdebug.dll
,然后放到扩展目录下。
修改 php.ini 文件,此处为初始配置,文章后面有完整配置
[XDebug]
xdebug.output_dir="D:\phpStudy\PHPTutorial\tmp\xdebug"
zend_extension=xdebug
xdebug.mode = debug
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9100
注意,xdebug3 和 xdebug2 的配置项发生了很大变化,尽量参考官方说明而不是网上copy。
文档:https://xdebug.org/docs/all_settings
php -m
查看 xdebug 的全部配置及默认值
php --ri xdebug
xdebug
__ __ _ _
\ \ / / | | | |
\ V / __| | ___| |__ _ _ __ _
> < / _` |/ _ \ '_ \| | | |/ _` |
/ . \ (_| | __/ |_) | |_| | (_| |
/_/ \_\__,_|\___|_.__/ \__,_|\__, |
__/ |
|___/
Version => 3.1.6
Support Xdebug on Patreon, GitHub, or as a business: https://xdebug.org/support
Enabled Features (through 'xdebug.mode' setting)
Feature => Enabled/Disabled
Development Helpers => ✘ disabled
Coverage => ✘ disabled
GC Stats => ✘ disabled
Profiler => ✘ disabled
Step Debugger => ✔ enabled
Tracing => ✘ disabled
Optional Features
Compressed File Support => no
Clock Source => GetSystemTimePreciseAsFileTime
Debugger => enabled
IDE Key =>
Directive => Local Value => Master Value
xdebug.mode => debug => debug
xdebug.start_with_request => default => default
xdebug.start_upon_error => default => default
xdebug.output_dir => D:\phpStudy\PHPTutorial\tmp\xdebug => D:\phpStudy\PHPTutorial\tmp\xdebug
xdebug.use_compression => 0 => 0
xdebug.trigger_value => no value => no value
xdebug.file_link_format => no value => no value
xdebug.filename_format => no value => no value
xdebug.log => no value => no value
xdebug.log_level => 7 => 7
xdebug.var_display_max_children => 128 => 128
xdebug.var_display_max_data => 512 => 512
xdebug.var_display_max_depth => 3 => 3
xdebug.max_nesting_level => 256 => 256
xdebug.cli_color => 0 => 0
xdebug.force_display_errors => Off => Off
xdebug.force_error_reporting => 0 => 0
xdebug.halt_level => 0 => 0
xdebug.max_stack_frames => -1 => -1
xdebug.show_error_trace => Off => Off
xdebug.show_exception_trace => Off => Off
xdebug.show_local_vars => Off => Off
xdebug.dump.COOKIE => no value => no value
xdebug.dump.ENV => no value => no value
xdebug.dump.FILES => no value => no value
xdebug.dump.GET => no value => no value
xdebug.dump.POST => no value => no value
xdebug.dump.REQUEST => no value => no value
xdebug.dump.SERVER => no value => no value
xdebug.dump.SESSION => no value => no value
xdebug.dump_globals => On => On
xdebug.dump_once => On => On
xdebug.dump_undefined => Off => Off
xdebug.profiler_output_name => cachegrind.out.%p => cachegrind.out.%p
xdebug.profiler_append => Off => Off
xdebug.cloud_id => no value => no value
xdebug.client_host => 127.0.0.1 => 127.0.0.1
xdebug.client_port => 9100 => 9100
xdebug.discover_client_host => Off => Off
xdebug.client_discovery_header => no value => no value
xdebug.idekey => no value => no value
xdebug.connect_timeout_ms => 200 => 200
xdebug.scream => Off => Off
xdebug.gc_stats_output_name => gcstats.%p => gcstats.%p
xdebug.trace_output_name => trace.%c => trace.%c
xdebug.trace_format => 0 => 0
xdebug.trace_options => 0 => 0
xdebug.collect_assignments => Off => Off
xdebug.collect_return => Off => Off
xdebug.auto_trace => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.collect_includes => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.collect_params => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.collect_vars => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.coverage_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.default_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.gc_stats_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.gc_stats_output_dir => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.overload_var_dump => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.profiler_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.profiler_enable_trigger => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.profiler_enable_trigger_value => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.profiler_output_dir => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_autostart => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_connect_back => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_host => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_log => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_log_level => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_mode => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_port => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_timeout => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.show_mem_delta => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.trace_output_dir => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.trace_enable_trigger => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.trace_enable_trigger_value => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
2、主要配置项说明
xdebug.mode
此设置控制启用哪些Xdebug功能。默认值为 develop
此设置只能在PHP进程启动时(直接或通过php-fpm)在php.ini或99-xdebug.ini读取的文件中进行设置,而不能在.htaccess和 .user.ini按请求读取的文件中进行设置。
接受以下值:
- off
没有启用任何功能。Xdebug除了检查功能是否已启用外不起作用。如果您想要接近零的开销,请使用此设置。 - develop
启用包括重载的var_dump()在内的开发帮助。 - coverage
使Code Coverage Analysis能够生成代码覆盖率报告,主要是与PHPUnit结合使用 。 - debug
启用步骤调试(Step Debugging)。这可用于在代码运行时逐步检查代码,并分析变量的值。 - gcstats
使垃圾收集统计信息能够收集有关PHP的垃圾收集机制的统计信息。 - profile
启用性能分析,您可以使用它通过KCacheGrind之类的工具分析性能瓶颈。 - trace
启用功能跟踪功能,该功能允许您记录每个函数调用,包括参数,变量赋值以及在对文件的请求期间进行的返回值。
您可以通过以逗号分隔xdebug.mode:的值作为标识符来同时启用多个模式xdebug.mode=develop,trace。
您还可以通过XDEBUG_MODE
在命令行上设置环境变量来设置模式,这将优先于xdebug.mode 设置。
start_with_request
默认值为 default
我们直到在CLI模式下,PHP脚本可以被轻松的调试,但是在fastcgi模式下,xdebug在何种情况拦截请求并触发调试呢,实际上一套WEB API 只有极少有性能瓶颈的API才需要被debug,每个接口都去记录profile是没必要的。这就是此配置的意义:
-
yes
该功能在PHP请求启动时以及运行任何PHP代码之前启动。例如,xdebug.mode = trace 和 xdebug.start_with_request = yes
启动整个请求的功能跟踪。 -
no
请求开始时,该功能未激活。你仍然可以调用函数xdebug_start_trace()
来启动 trace;调用xdebug_break()
开始逐步调试;调用xdebug_start_gcstats()
来做垃圾收集统计。 -
trigger
仅当请求开始时存在特定触发条件时,才激活该功能。触发器的名称是(大写)XDEBUG_TRIGGER
,而Xdebug会去检查一下变量中($_ENV,$_GET,$_POST,$_COOKIE
)是否有这个字段。
功能特定的触发器名称也有一个旧式的备用名称:(XDEBUG_PROFILE 对于 Profiling),(XDEBUG_TRACE 对于 Function Trace)和 (XDEBUG_SESSION 对于 Step Debugging)
。
也可以通过进行步骤调试的调试会话管理XDEBUG_SESSION_START
。
使用xdebug.trigger_value
可以控制哪个特定的触发器值将激活该触发器。如果xdebug.trigger_value
设置为空字符串,则将接受任何值。
示例:http://localhost:8000/test.php?XDEBUG_TRIGGER
-
default
该default值取决于xdebug.mode
:
debug
:trigger
gcstats
:no
profile
:yes
trace
:trigger
client_port
Xdebug’s default debugging port has changed from 9000 to 9003.
php 的 xdebug 扩展作为一个代理客户端,会去连接 xdebug.client_host : xdebug.client_port
,此时服务端是编辑器,因此编辑器要先启动,比如,如果你设置xdebug.mode=debug
和xdebug.start_with_request=yes
,那么每次运行php程序xdebug都会去连接服务端,如果你没有启动编辑器的调试服务,那么php就会抛出错误信息Xdebug: [Step Debug] Time-out connecting to debugging client, waited: 200 ms. Tried: 127.0.0.1:9100 (through xdebug.client_host/xdebug.client_port) :-(
根据以上说明,我们的主要两个配置的值如下,这是下文的前提
xdebug.mode = debug
xdebug.start_with_request = trigger
因此,无论是命令行下还是web下的调试都需要人为来触发,实际上,这是合理的,因为大部分时候我们是不需要调试的,一直开启xdebug没有必要,人为触发的方式参考 https://xdebug.org/docs/step_debug
关于如何来人为触发,我们先配置好编辑器再说。
3、vscode安装 PHP Debug
插件。
4、vscode配置调试
查看 -> 命令面板 -> 搜索 settings 打开 settings.json
配置PHP执行程序:
"php.validate.executablePath": "D:/phpStudy/PHPTutorial/php/php-7.2.31-nts-Win32-VC15-x64/php.exe",
这个是全局配置,跟随用户走的。
在当前项目目录下,创建 launch.js 文件 ,注意,这个文件是保存在项目下的,而不是全局的,每个项目都要单独配置。
如果已经存在了就修改
点击创建 launch.json 文件
之后,选择 PHP,那么 PHP Debug 插件就会在当前项目中创建好 launch.json 文件,只需要将 port 修改为 9100 即可。
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9100
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 9100,
"runtimeArgs": [
"-dxdebug.start_with_request=yes"
],
"env": {
"XDEBUG_MODE": "debug,develop",
"XDEBUG_CONFIG": "client_port=${port}",
"XDEBUG_SESSION": "1"
}
},
{
"name": "Launch Built-in web server",
"type": "php",
"request": "launch",
"runtimeArgs": [
"-dxdebug.mode=debug",
"-dxdebug.start_with_request=yes",
"-S",
"localhost:0"
],
"program": "",
"cwd": "${workspaceRoot}",
"port": 9100,
"serverReadyAction": {
"pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
"uriFormat": "http://localhost:%s",
"action": "openExternally"
}
}
]
}
新建测试文件 test.php
<?php
$str = md5('123456');
echo date('Y-m-d H:i:s', 1596211200);
echo PHP_EOL;
echo date('Y-m-d H:i:s', 1604160000);
launch.json 提供了三种调试模式,下面将分别演示和说明
Listen for Xdebug
编辑器只管启动监听,就等着 php xdebug 扩展过来连接并进行交互。然后你就可以在命令行执行 php xxx.php
来开始调试,或者也可以用来调试http请求。
在 test.php 打断点,最后按 F5 或者点击上面的绿色箭头启动调试,最下面的横条会变成黄色,效果如下:
我们打开编辑器命令行,执行php test.php
,发现并没有触发调试,因为我们是需要手动来触发的。还是在当前命令行,我们来设置一个临时的环境变量,因为上面说了xdebug会在$_ENV中查找特定标志。
在 windows 命令行中设置临时环境变量使用set XDEBUG_SESSION=1
,而在 linux 下则是export XDEBUG_SESSION=1
,注意,此种方式设置的环境变量是临时的,并且只在当前命令行可见。我们通过echo %XDEBUG_SESSION%
来查看此临时环境变量。
> echo %XDEBUG_SESSION%
%XDEBUG_SESSION%
> set XDEBUG_SESSION=1
> echo %XDEBUG_SESSION%
1
再次执行php test.php
就可以调试了,按 F5 下一步,将鼠标移到变量上面就可以看到变量的值,当然,左边也会列出变量的值。
程序执行完了,而调试服务依然在监听。
如果此时 set XDEBUG_SESSION=0
,运行脚本,发现还是能够调试,说明 xdebug 只是检测有没有 XDEBUG_SESSION
,而不关心其值。
如果闲麻烦可以直接 php -dxdebug.start_with_request=yes test.php
接下来,我们调试 http 请求。
在当前目录,启动php内置的web服务器 php -S localhost:8000
> php -S localhost:8000
PHP 7.2.31 Development Server started at Wed Aug 16 15:56:48 2023
Listening on http://localhost:8000
Document root is D:\dev\php\magook\trunk\server\test_debug
Press Ctrl-C to quit.
浏览器访问 http://localhost:8000/test.php
,发现已经可以调试了,这是怎么做到的?还是因为我们是在当前命令行启动的php server,自带有 XDEBUG_SESSION
环境变量。
我直接在编辑器中新起一个命令行就不会有 XDEBUG_SESSION
再次访问 http://localhost:8000/test.php
,就没有调试了。想要有调试,可以在 $_GET, $_POST, $_COOKIE
中带有XDEBUG_SESSION=xxx
,这个值不做限制,除非你设置了 xdebug.trigger_value
。
访问 http://localhost:8000/test.php?XDEBUG_SESSION=xxx
就可以调试了。
好,关闭调试监听。
Launch currently open script
这是针对当前脚本的快速调试,不用我们在命令行自己输入执行。我们来分析一下这个配置
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 9100,
"runtimeArgs": [
"-dxdebug.start_with_request=yes"
],
"env": {
"XDEBUG_MODE": "debug,develop",
"XDEBUG_CONFIG": "client_port=${port}"
}
}
它首先设置了两个环境变量(XDEBUG_MODE, XDEBUG_CONFIG
),当然也是当前命令行的临时变量,xdebug会接收它们并临时修改配置。
然后在执行的时候修改了php的配置,最终的执行命令是:
php -dxdebug.start_with_request=yes test.php
因为它会使用新的命令行来执行这个命令,所以也不会带有XDEBUG_SESSION
我们定位到 test.php
文件,选择Launch currently open script
,点击开始调试。效果如下
嗯?为什么没看见输出呢,那是因为没有打开调试控制台
Launch Built-in web server
从配置上来看,它就是启动了 php 内置的 server 来提供服务,并开启调试。
修改 localhost:8000
{
"name": "Launch Built-in web server",
"type": "php",
"request": "launch",
"runtimeArgs": [
"-dxdebug.mode=debug",
"-dxdebug.start_with_request=yes",
"-S",
"localhost:8000"
],
"program": "",
"cwd": "${workspaceRoot}",
"port": 9100,
"serverReadyAction": {
"pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
"uriFormat": "http://localhost:%s",
"action": "openExternally"
}
}
最终内部执行的命令为
php -dxdebug.mode=debug -dxdebug.start_with_request=yes -S localhost:8000
从配置就能看出这是强制调试的,不需要任何触发。
访问 http://localhost:8000/test.php
5、trace & profile
xdebug.mode = debug
的使用场景是当你需要断点调试的时候,但是有时候程序没问题,你想看看性能和各个地方的耗时情况,此时你可以换成 trace 和 profile 模式。
记录 trace ,也就是函数调用链,修改 php.ini
xdebug.mode = trace
还是上面的脚本,去掉所有的断点(当然也可以不去掉但是你需要一直按 F5 直到程序跑完),此时按一下 F5 程序就跑完了,然后去到目录D:\phpStudy\PHPTutorial\tmp\xdebug
也就是上面配置的目录,会看到多出了一个文件 trace.1480951438.xt
,内容为:
TRACE START [2020-12-30 06:47:20.891508]
0.0782 383264 -> {main}() D:\dev\php\my\test.php:0
0.0784 383264 -> ini_set($varname = 'date.timezone', $newvalue = 'Asia/Shanghai') D:\dev\php\my\test.php:2
1.3640 383536 -> getstr() D:\dev\php\my\test.php:15
1.9238 383536 -> rand($min = 0, $max = 25) D:\dev\php\my\test.php:6
2.2915 383536 -> date($format = 'Y-m-d H:i:s', $timestamp = 1596211200) D:\dev\php\my\test.php:17
3.0697 384248 -> date($format = 'Y-m-d H:i:s', $timestamp = 1604160000) D:\dev\php\my\test.php:19
3.0722 313408
TRACE END [2020-12-30 06:47:23.885604]
另外你还可以记录 profile ,用以性能分析,修改 php.ini
xdebug.mode = profile
于是多出了一个文件 cachegrind.out.241668
,内容为
version: 1
creator: xdebug 3.0.1 (PHP 7.2.21)
cmd: D:\dev\php\my\test.php
part: 1
positions: line
events: Time_(10ns) Memory_(bytes)
fl=(1) php:internal
fn=(1) php::ini_set
2 9970 272
fl=(1)
fn=(2) php::rand
6 1170 0
fl=(2) D:\dev\php\my\test.php
fn=(3) getstr
4 52017740 0
cfl=(1)
cfn=(2)
calls=1 0 0
6 1170 0
fl=(1)
fn=(4) php::date
17 7210 936
fl=(1)
fn=(4)
19 1200 256
fl=(2)
fn=(5) {main}
1 455536270 0
cfl=(1)
cfn=(1)
calls=1 0 0
2 9970 272
cfl=(2)
cfn=(3)
calls=1 0 0
15 52018910 0
cfl=(1)
cfn=(4)
calls=1 0 0
17 7210 936
cfl=(1)
cfn=(4)
calls=1 0 0
19 1200 256
summary: 507821490 418632
那么如何从这个文件分析出程序的性能呢?由于篇幅过长,我写到了另外一篇文章来讲这个 https://blog.csdn.net/raoxiaoya/article/details/111994696。
在使用浏览器这种方式调试的过程中发现一个问题,只有第一次请求才会生成 cachegrind.out
文件,后面的请求并不会去更新此文件,即使我改了代码,暂时不知道什么原因。
关于在PHPStorm中使用xdebug可以参考 https://blog.csdn.net/qq_34087545/article/details/89490269
关于XDebug的运行原理
根据上面的两种调试情况,我画了一个简易流程图。