PHP如何打印跟踪调试信息

对于大部分编译型语言来说,比如 C 、 Java 、 C# ,我们都能很方便地进行断点调试,但是 PHP 则必须安装 XDebug 并且在编辑器中进行复杂的配置才能实现断点调试的能力。不过,如果只是简单的调试并且查看堆栈回溯的话,其实 PHP 已经为我们准备好了两个函数,能够让我们非常方便的看到程序运行时的调用情况。

debug_backtrace()

从这个方法的字面意思上就可以看出,它的意思就是调试回溯,返回的也正是一段回溯信息的数组。

  1. function a_test($str)
  2. {
  3. echo "Hi: $str", PHP_EOL;
  4. var_dump(debug_backtrace());
  5. }
  6. var_dump(debug_backtrace());
  7. a_test("A");
  8. // Hi: A/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php:7:
  9. // array(1) {
  10. // [0] =>
  11. // array(4) {
  12. // 'file' =>
  13. // string(93) "/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php"
  14. // 'line' =>
  15. // int(12)
  16. // 'function' =>
  17. // string(6) "a_test"
  18. // 'args' =>
  19. // array(1) {
  20. // [0] =>
  21. // string(1) "A"
  22. // }
  23. // }
  24. // }

这个方法必须在函数中调用,在函数方法外部使用是不会有内容的。从内容中看,它输出了关于这个函数的 __FILE__ 、 __LINE__ 、 __FUNCTION__ 、$argv 等信息。其实就是关于当前打印这行所在函数的相关内容。

我们当然也可以多嵌套几层函数来看一下打印出的内容是什么。

  1. function b_test(){
  2. c_test();
  3. }
  4. function c_test(){
  5. a_test("b -> c -> a");
  6. }
  7. b_test();
  8. // Hi: b -> c -> a
  9. // /Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php:7:
  10. // array(3) {
  11. // [0] =>
  12. // array(4) {
  13. // 'file' =>
  14. // string(93) "/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php"
  15. // 'line' =>
  16. // int(37)
  17. // 'function' =>
  18. // string(6) "a_test"
  19. // 'args' =>
  20. // array(1) {
  21. // [0] =>
  22. // string(11) "b -> c -> a"
  23. // }
  24. // }
  25. // [1] =>
  26. // array(4) {
  27. // 'file' =>
  28. // string(93) "/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php"
  29. // 'line' =>
  30. // int(33)
  31. // 'function' =>
  32. // string(6) "c_test"
  33. // 'args' =>
  34. // array(0) {
  35. // }
  36. // }
  37. // [2] =>
  38. // array(4) {
  39. // 'file' =>
  40. // string(93) "/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php"
  41. // 'line' =>
  42. // int(40)
  43. // 'function' =>
  44. // string(6) "b_test"
  45. // 'args' =>
  46. // array(0) {
  47. // }
  48. // }
  49. // }

没错,数组的输出顺序就是一个栈的执行顺序,b_test() 最先调用,所以它在栈底,对应的输出也就是数组中的最后一个元素。

在类中也是类似的使用方法。

  1. class A{
  2. function test_a(){
  3. $this->test_b();
  4. }
  5. function test_b(){
  6. var_dump(debug_backtrace());
  7. }
  8. }
  9. $a = new A();
  10. $a->test_a();
  11. // /Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php:90:
  12. // array(2) {
  13. // [0] =>
  14. // array(7) {
  15. // 'file' =>
  16. // string(93) "/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php"
  17. // 'line' =>
  18. // int(87)
  19. // 'function' =>
  20. // string(6) "test_b"
  21. // 'class' =>
  22. // string(1) "A"
  23. // 'object' =>
  24. // class A#1 (0) {
  25. // }
  26. // 'type' =>
  27. // string(2) "->"
  28. // 'args' =>
  29. // array(0) {
  30. // }
  31. // }
  32. // [1] =>
  33. // array(7) {
  34. // 'file' =>
  35. // string(93) "/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php"
  36. // 'line' =>
  37. // int(95)
  38. // 'function' =>
  39. // string(6) "test_a"
  40. // 'class' =>
  41. // string(1) "A"
  42. // 'object' =>
  43. // class A#1 (0) {
  44. // }
  45. // 'type' =>
  46. // string(2) "->"
  47. // 'args' =>
  48. // array(0) {
  49. // }
  50. // }
  51. // }

在类中使用的时候,在数组项中会多出一个 object 字段,显示的是这个方法所在类的信息。

debug_backtrace() 的函数声明是:

debug_backtrace ([ int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT [, int $limit = 0 ]] ) : array

其中 options 是有两个常量可以定义,DEBUG_BACKTRACE_PROVIDE_OBJECT 表明是否填充 "object" 的索引;DEBUG_BACKTRACE_IGNORE_ARGS 是否忽略 "args" 的索引,包括所有的 function/method 的参数,能够节省内存开销。limits 可用于限制返回堆栈帧的数量,默认为0返回所有的堆栈。

debug_backtrace() 以及下面要介绍的 debug_print_backtrace() 方法都是支持 require/include 文件以及 eval() 中的代码的,在嵌入文件时,会输出嵌入文件的路径,这个大家可以自行尝试。

debug_print_backtrace()

这个方法从名称也可以看出,它会直接打印回溯内容,它的函数声明和 debug_backtrace() 是一样的,不过 $options 默认是 DEBUG_BACKTRACE_IGNORE_ARGS ,也就是说,它只打印调用所在文件及行数。

  1. function a() {
  2. b();
  3. }
  4. function b() {
  5. c();
  6. }
  7. function c(){
  8. debug_print_backtrace();
  9. }
  10. a();

#0 c() called at [/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php:144]

#1 b() called at [/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php:140]

#2 a() called at [/Users/zhangyue/MyDoc/博客文章/dev-blog/php/202004/source/PHP打印跟踪调试信息.php:151]

另外就是这个函数不需要使用 var_dump() 或 print_r() 进行输出,直接使用这个函数就会进行输出。能够非常快捷方便的让我们进行调试,比如在 laravel 这类大型框架中,我们在控制器需要查看堆栈信息时,就可以使用 debug_print_backtrace() 快速地查看当前的堆栈调用情况。而 debug_backtrace() 如果没有指定 $options 的话,则会占用非常大的内存容量或者无法完整显示。

总结

今天介绍的这两个函数能够灵活地帮助我们调试代码或者了解一个框架的调用情况。当然,在正式的情况下还是推荐使用 Xdebug 加上编辑器的支持来进行断点调试,因为使用 debug_backtrace() 这两个方法我们无法看到变量的变化情况。

测试代码:

github.com/zhangyue050…