对于 PHP 开发者来说,单步的断点 Debug 调试并不是咱们的必修课,而 Java、C#、C++ 这些动态语言则会经常性地进行这种调试。其实,咱们 PHP 也是反对这类调试形式的,特地是对于理解一些开源框架,或者有十分深层次的 Bug 跟踪时,断点调试会十分有用。
不少接触过 PHP 断点调试的肯定都用过鼎鼎大名的 XDebug。不过咱们明天讲的并不是这款扩大,而是另一个曾经集成到 PHP 官网源码中的调试工具,并且,最重要的是,它调试时看到的内容是更为底层的 opcode 执行过程。话不多说,咱们间接进入到 phpdbg 这款工具的学习中吧!!
phpdbg 命令行性能
在咱们装置好 PHP 后,默认就有了 phpdbg 这个工具。间接在命令行运行就会进入这个工具。
% phpdbg
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
没错,它就是随 PHP 装置的时候默认自带的,如果你的环境变量中没有这个工具命令的话,能够在 PHP 装置目录的 bin/ 目录上面找到。
在进入 phpdbg 环境后,咱们应用 help 就能够查看它的操作阐明。
prompt> help
phpdbg is a lightweight, powerful and easy to use debugging platform for PHP5.4+
It supports the following commands:
Information
list list PHP source
info displays information on the debug session
print show opcodes
frame select a stack frame and print a stack frame summary
generator show active generators or select a generator frame
back shows the current backtrace
help provide help on a topic
……
帮忙文档十分长,大家能够本人查看具体的内容,其中有一个 help 命令能够让咱们看到许多简写的命令,咱们次要应用这些简写的命令别名就能够。
prompt> help aliases
Below are the aliased, short versions of all supported commands
e exec set execution context
s step step through execution
c continue continue execution
r run attempt execution
u until continue past the current l
命令的简介和查看都很简略,那么咱们要如何来调试 PHP 文件呢?这个才是咱们最关怀的事件。在调试一个文件的时候,咱们须要将它载入到以后的执行环境中。能够在以后 phpdbg 环境中应用 e 命令指定文件进行载入,也能够在运行 phpdbg 的时候通过 -e 来指定须要载入的文件。
% phpdbg -e PHPDebug 互动扩大.php
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
prompt>
这里咱们应用的是第二种形式,在启动 phpdbg 时应用 -e 参数来指定须要载入的文件。
一般断点设置
载入了文件,进入了命令行,咱们就能够进行断点调试了。首先,咱们应用代码形式来设置断点。在下面的测试文件中,咱们应用上面的形式来定义断点。
echo 111;
phpdbg_break_file("PHPDebug 互动扩大.php", 3);
echo 222;
phpdbg_break_file("PHPDebug 互动扩大.php", 6);
phpdbg_break_file() 函数就是来定义断点的,它有两个参数,第一个参数是文件名,这个不能乱填。第二个参数是断点的行号。
接下来,在命令行中,咱们运行两次简写的 run 命令 r。
prompt> r
111
[Breakpoint #0 added at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:3]
222
[Breakpoint #1 added at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:6]
[Script ended normally]
prompt> r
[Breakpoint #0 at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:3, hits: 1]
>00003: echo 111;
00004: phpdbg_break_file("PHPDebug 互动扩大.php", 3);
00005:
prompt>
能够看出,在第一次运行 r 的时候,phpdbg 将整个文件进行了一次扫描并输入了以后的两个断点信息。而后再运行一次 r 则定位到了第 3 行,也就是第一个断点的地位。接下来,咱们就要进行单步调试了,咱们间接应用 step 的简写命令 s。
prompt> s
[L3 0x10ecae220 ECHO 111 /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
111
[L4 0x10ecae240 EXT_STMT /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
>00004: phpdbg_break_file("PHPDebug 互动扩大.php", 3);
00005:
00006: echo 222;
prompt>
断点地位向下运行了,果然是合乎咱们的预期,开始了一行一行的单步运行。在下面输入的内容中,咱们看到了 opcode 运行的状态。比方 L3 0x10ecae220 ECHO 这一行指的就是第 3 行执行了 ECHO 操作。是不是感觉十分高大上。
一路 s 下来,走到最初咱们就完结了这次断点调试,phpdbg 环境将退出 run 运行时。
……
prompt> s
[Script ended normally]
prompt> s
[Not running]
prompt>
这样,一趟调试就实现了。当咱们在第一个断点不想单步调试,想间接进入下一个断点,就能够应用 continue 的简写命令 c 来间接跳到下一个断点。
prompt> r
Do you really want to restart execution? (type y or n): y
[Breakpoint #0 at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:3, hits: 1]
>00003: echo 111;
00004: phpdbg_break_file("PHPDebug 互动扩大.php", 3);
00005:
prompt> c
111
[Breakpoint at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:3 exists]
[Breakpoint #1 at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:6, hits: 1]
>00006: echo 222;
00007: phpdbg_break_file("PHPDebug 互动扩大.php", 6);
00008:
prompt>
另外还有一个命令就是能够间接看到以后载入的文件环境中的所有断点信息的。
prompt> info break
------------------------------------------------
File Breakpoints:
#0 /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:3
#1 /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:6
以上,就是一个简略的行断点设置以及调试步骤。当然,咱们只是针对一个简略文件的测试,对于简单的框架型零碎,断点的设置和调试就会简单很多,不过相应地,咱们可能看到底层的 opcode 代码的执行状况,也能让咱们对所测试的内容有更加深刻的理解。
办法断点及运行步骤剖析
接下来咱们来设置一个办法断点,并一步步察看 opcode 的状况。
$i = 1;
// phpdbg -e PHP\ Debug 互动扩大.php
function testFunc(){
global $i;
$i += 3;
echo "This is testFunc! i:" . $i, PHP_EOL;
}
testFunc();
phpdbg_break_function('testFunc');
在 PHP 代码中,咱们应用 phpdbg_break_function() 来给这个 testFunc() 办法设置一个断点。当代码中调用这个函数的时候,就会进入这个断点中。
prompt> r
[Breakpoint #0 in testFunc() at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:11, hits: 1]
>00011: function testFunc(){
00012: global $i;
00013: $i += 3;
prompt> s
[L12 0x109eef620 EXT_STMT /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
>00012: global $i;
00013: $i += 3;
00014: echo "This is testFunc! i:" . $i, PHP_EOL;
prompt> s
[L12 0x109eef640 BIND_GLOBAL $i "i" /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
[L13 0x109eef660 EXT_STMT /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
>00013: $i += 3;
00014: echo "This is testFunc! i:" . $i, PHP_EOL;
00015: }
间接进行了两次 s 单步,能够看到 global $i 对应的 opcode 操作是 BIND_GLOBAL。持续向下操作。
prompt> s
[L13 0x109eef680 ASSIGN_ADD $i 3 /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
[L14 0x109eef6a0 EXT_STMT /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
>00014: echo "This is testFunc! i:" . $i, PHP_EOL;
00015: }
00016:
prompt> s
[L14 0x109eef6c0 CONCAT "This is testFunc!"+ $i ~1 /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
[L14 0x109eef6e0 ECHO ~1 /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
This is testFunc! i:4
[L14 0x109eef700 EXT_STMT /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
[L14 0x109eef720 ECHO "\n" /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
[L15 0x109eef740 EXT_STMT /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php]
>00015: }
00016:
00017: testFunc();
第 13 行执行的是 $i += 3 的操作,对应的 opcode 操作是 ASSIGN_ADD,减少的值是 3。持续 s 后执行了第 14 行,留神这里进行了两步操作。一次是 CONCAT,一次是 ECHO,而后代码失常输入了打印进去的语句。
从下面的几步调试能够清晰的看到 PHP 在 opcode 层面的一步步的执行状态,就像 XDebug 一样,每一次的执行都会有相干的变量、操作的信息输入。
类函数断点设置
类函数的断点设置其实就和下面的办法断点函数一样,十分的简略不便。
class A{function testFuncA(){echo "This is class A testFuncA!", PHP_EOL;}
}
$a = new A;
$a->testFuncA();
phpdbg_break_method('A', 'testFuncA');
这里就不贴出调试的代码了,大家能够本人尝试一下。
命令行减少断点
除了在 PHP 代码中给出固定的断点之外,咱们还能够在命令行中进行断点的减少,比方咱们去掉之前的办法断点函数。而后在命令行中指定在办法中减少一个断点。
prompt> b testFunc#3
[Breakpoint #1 added at testFunc#3]
3 这是什么意思呢?其实就是说咱们在这个办法体外部的第 3 行减少一个断点。也就是说,咱们在 $i += 3; 这一行减少了一个断点。行数是从办法定义那一行开始算的并且是从 1 开始,如果不加这个行数,就是间接从办法定义那一行开始。
prompt> r
[Breakpoint #0 resolved at testFunc#3 (opline 0x1050ef660)]
[Breakpoint #0 resolved at testFunc#3 (opline 0x1050ef660)]
[Breakpoint #0 resolved at testFunc#3 (opline 0x1050ef660)]
[Breakpoint #0 resolved at testFunc#3 (opline 0x1050ef660)]
[Breakpoint #0 in testFunc()#3 at /Users/zhangyue/MyDoc/ 博客文章 /dev-blog/php/202006/source/PHPDebug 互动扩大.php:13, hits: 1]
>00013: $i += 3;
00014: echo "This is testFunc! i:" . $i, PHP_EOL;
00015: }
执行 r 后,咱们就间接定位到了 testFun() 办法中的第三行。
总结
明天咱们只是简略的学习了一下 phpdbg 这个工具的应用。从 help 命令中就能够看出,这个工具还有十分多的选项参数,能够帮咱们实现许多调试工作。在这里只是跟大家一起入个门,未来在学习的过程中再次接触到的时候咱们再持续深刻的钻研。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202006/source/PHPDebug%E4%BA%92%E5%8A%A8%E6%89%A9%E5%B1%95.php
参考文档:
https://www.php.net/manual/zh/intro.phpdbg.php
各自媒体平台均可搜寻【硬核项目经理】