PHP调试技巧

任何开发软件的人迟早都想调试他们的代码。非常明确地看到您的代码逐步执行并在运行时检查变量的值。使用PHP进行开发也不例外。在本文中,我们将介绍PHP调试,并且还将提供一些提示和技巧。

调试

“虫子”这个词最早是由托马斯·爱迪生使用的,但是在1947年由著名的格雷斯·霍珀(Grace Hopper)推广,当时在哈佛大学的马克二世计算机中,一只真正的飞蛾从一个接力器中被移除。他们甚至把它录到了原木上。

调试

今天,我们将调试定义为运行应用程序,同时检查现有变量并查看当前运行的指令。这意味着应用程序在继续执行下一个语句之前等待开发人员发出命令。通常,这发生在编辑器或IDE中。我们称之为交互式调试。

将日志语句添加到代码并在应用程序运行后进行检查时,会发生非交互式调试。这是一种粗略的调试方式,但不一定是个坏主意。例如,众所周知,多线程应用程序难以以交互方式进行调试。使用日志来识别哪个线程实际上非常有用。

PHP调试

作为一个起点,我们将采用这个简单的PHP文件:





你的名字:


你的年龄:





你好




<?PHP
function getMessage($age){
if($age <60){
回归'你是'。 (int)$_ POST('age')。 '岁。';
} else {
回归'你是一个受人尊敬的年龄。';
}
}

if($_POST){
$message = getMessage($_ POST('age'));
echo $message;
}
?>




这将呈现一个简单的表单,在发布时会生成以下结果(为清楚起见,我再次输入了已发布的值):

记录到输出

让我们从最简单的(非交互式)PHP调试形式开始:在某处编写输出,以便我们稍后阅读。在PHP中执行此操作的最简单方法是写入PHP呈现的文档。在我们的示例中,我们可以简单地在代码中添加这样的内容:

< - 发布:
<?PHP
if($_POST){
echo var_dump($_ POST);
}
?>
- >

请注意我们如何将我们的PHP代码盘点在HTML注释中。这使页面看起来与以前一样。但是,在底层源代码中,我们现在可以看到“var_dump”调用的结果。这就是我的浏览器开发人员工具中的样子:

当然,这些信息仍然发送给客户端,这并不理想。您可能会向意图不好的人泄露有关您的应用程序的过多信息。

这种技术有变化。例如,有些工具会将某些信息写入浏览器的控制台而不是HTML输出,但它们都涉及将数据发送到客户端。

另一个缺点是需要在运行应用程序之前编写语句。例如,如果我们忘记“var_dump”变量,我们需要更改代码并再次运行应用程序。

您的开发环境

在继续PHP调试之前,我想看看我们的开发环境。 PHP使用php.ini文件来控制其行为。当你安装PHP时,它通常附带一个php.ini-development和一个php.ini-production文件。在本地计算机或开发服务器上,您可以安全地将php.ini-development重命名为php.ini。此配置将显示比生产环境中所需的信息更多的信息。但是,当您开发时,它是有用的信息。在生产服务器上,您将要使用php.ini-production文件。

一个这样的设置是“display_errors”指令。这意味着在执行应用程序期间抛出的任何异常都将呈现给用户。例如,如果我们在“getMessage”函数中抛出异常,我们会看到如下内容:

另一个有趣的设置是“error_reporting”指令。在开发中,它设置为“E_ALL”,这意味着PHP将报告任何异常情况。这包括最佳实践和编码标准。在生产环境中,这些检查会使您的应用程序过于缓慢。这就是为什么php.ini的生产版本会排除一些错误。

如果您的更改似乎没有任何效果,请重新启动Web服务器。当您在Windows上的IIS上运行PHP时似乎没有必要,但您的体验可能会有所不同。

有一个php.ini用于开发目的,一个用于生产服务器是很重要的。通过拆分配置,我们可以访问有用的信息,而不会将其暴露给外部世界。现在让我们继续调试。

使用Xdebug

Xdebug是一个PHP调试工具,已经存在了很长一段时间。有替代品,但Xdebug似乎非常受欢迎,积极维护,并具有良好的文档。因为PHP在不同的平台上运行,所以我建议使用Xdebug向导来了解要安装的版本以及安装方式。一旦安装完毕,我们就可以开始使用了它。

阅读关于堆栈跟踪的Xdebug文档很有意思。您可以将其配置为在出现问题时呈现大量有用的信息。例如,这是我已经添加到我的php.ini文件来配置Xdebug:

xdebug.collect_params = 4;显示有关传递给函数的参数的完整信息
xdebug.show_local_vars = on;显示本地范围内的所有变量
xdebug.dump.SERVER = REQUEST_URI,DOCUMENT_ROOT,SCRIPT_FILENAME;显示这些服务器变量
xdebug.default_enable = 1;启用Xdebug

我们现在的错误显示了更多可用于查明问题的信息。

PHP调试Xdebug

我们现在可以看到调用堆栈,局部变量,服务器变量以及我们传递给函数的内容。

使用Xdebug进行交互式调试

现在我们已经设置了非交互式调试,让我们来看看交互式调试。这也称为远程调试。

Xdebug允许调试客户端使用DBGp协议连接到它。这对我们来说并不重要,除了它允许您使用几乎任何IDE或编辑器,只要它(或插件)支持此协议。大多数主要编辑都应该能够使用Xdebug调试PHP。查看Xdebug网站上的概述,了解支持的客户列表。但即使你没有看到自己喜欢的编辑器,它仍然可能。例如,没有提到Visual Studio Code,但是有一个PHP Debugging扩展可用。

要允许远程调试,我们需要将这两行添加到php.ini文件中:

xdebug.remote_enable = 1;启用远程调试
xdebug.remote_autostart = 1;自动启动,因此我们不必先发出特殊的HTTP请求

您很可能必须先配置编辑器才能连接到Xdebug。查看编辑器的文档以了解具体方法。

如果您已使用上述设置,则编辑器必须连接到默认端口9000.然后,您可以在编辑器中以交互方式进行调试。例如,这是我在Visual Studio代码中调试我们的应用程序:

交互式调试Xdebug

我们现在可以在断点处停下来检查本地和全局变量,逐步完成我们的应用程序,甚至修改变量。在上面的屏幕截图中,我将鼠标悬停在“$age”变量上,我们可以看到它的值。

Xdebug将为我们的每个请求连接到我们的编辑器。只要我们的编辑器正在监听Xdebug,我们就可以调试每个请求。

这是一种更强大的调试方法,而不是将消息写入我们正在呈现的HTML。

PHP调试技巧和窍门

现在我们已经介绍了如何调试PHP的基础知识,让我们来看看一些提示和技巧。我们实际上已经涵盖了三个:

  • 使用php.ini在开发时公开信息,但在生产服务器上避免这种情况。
  • 使PHP显示错误。
  • 使用Xdebug而不是将消息写入输出。

现在为一些额外的。

痕迹

有时,在不使用远程调试器的情况下查看请求的完整跟踪会很有用。例如,您可能希望概览所有拨打的电话,以便日后进行分析。 Xdebug允许我们通过将这些设置添加到php.ini来创建函数跟踪:

xdebug.auto_trace = 1;立即开始跟踪
xdebug.trace_output_dir = C: PHP72 traces;写入跟踪的位置(必须存在)

为简单起见,我将“auto_trace”设置为“1”,以便Xdebug立即开始跟踪我们的所有请求。如果您需要auto_prepend_file中任何代码的跟踪,这将非常有用。

跟踪所有内容很容易,但如果我们告诉Xdebug为每个请求创建一个新文件,它会导致很多跟踪文件,我们稍后会看到。如果您不想跟踪每个请求,请删除“auto_trace”设置并添加以下内容:

xdebug.trace_enable_trigger = 1;允许触发跟踪
xdebug.trace_enable_trigger_value =“secret”;仅在提供此秘密时才显示

这样,对“index.php”的调用不会触发Xdebug写入跟踪。但是调用“index.php?XDEBUG_TRACE = secret”会。您也可以在cookie中提供。这似乎是一种笨拙的做事方式。幸运的是,有一些浏览器扩展可以帮助您。例如,Chrome的Xdebug帮助程序提供了一个简单的按钮,可帮助您打开或关闭跟踪。

跟踪示例

最后一个有趣的设置是“trace_output_name。”使用我们当前的配置,Xdebug将始终覆盖相同的文件。这就是我们可以添加此设置的原因:

xdebug.trace_output_name =跟踪。%u

Xdebug现在将创建一个文件名中带有时间戳的新文件。

对于这个例子,我人为地使我们的应用程序更复杂,以呈现如下所示的跟踪:

TRACE START(2019-03-21 10:42:02)
0.4024 419112 - > {main}()C: phpdebugging index.php:0
0.4024 419112 - > htmlspecialchars('Peter')C: phpdebugging index.php:10
0.4025 419112 - > level1()C: phpdebugging index.php:41
0.4025 419112 - > level2()C: phpdebugging index.php:24
0.4025 419112 - > level3()C: phpdebugging index.php:28
0.4026 419112 - > level3b()C: phpdebugging index.php:29
0.4026 419112 - > getMessage($age ='8')C: phpdebugging index.php:42
0.4027 333064
TRACE END(2019-03-21 10:42:02)

这是一个简单的例子,但痕迹很快就会变得非常大。这里是Drupal跟踪的一部分截图,例如:

Drupal Trace

第一列显示自跟踪开始以来的时间(以毫秒为单位)。第二列显示该时间点的内存使用情况。

如果将“xdebug.show_mem_delta”添加到php.ini文件中,您将获得一个额外的列,显示内存消耗的差异。如果您正在尝试查找使用过多内存的函数,这将非常有用。

还有“xdebug.collect_return”,它将每个函数的返回值添加到跟踪中。

最后,“xdebug.collect_params”设置也会影响跟踪输出。它确定显示有关传递给函数的参数的信息量。此设置也会影响Xdebug分析器,我们将在下面看到。

剖析

分析类似于跟踪,但与跟踪略有不同。它与跟踪相同,但它将信息存储在某个应用程序可以打开的文件中,以向您显示有用的概述。它还提供了一种方法,可以查看调用函数的次数以及函数执行与整个请求相关的时间。

要在Xdebug中启用分析,您可以添加与跟踪设置类似的设置。

xdebug.profiler_enable = 1
xdebug.profiler_output_dir = C: PHP72 profiler;确保此目录存在
xdebug.profiler_output_name =轮廓。%u

也可以将分析配置为使用GET / POST参数或cookie,就像跟踪一样。您也可以在这里使用浏览器扩展。

我通过添加一些人工慢的函数使我们的应用程序再次变得更复杂。发出请求后,我指定的目录中有一个新文件。有几个工具可以打开PHP分析文件。看看Xdebug文档。我在PHPStorm中打开了我们的个人资料文件:

PHPstorm

同样,这是一个简单的例子。但在实际应用中,它可以为您提供有用的见解。

在上面的概述中,您可以看到

  • 被调用者:函数调用了哪些函数(即函数的“子”)。
  • 调用者:哪些函数调用当前选择的函数(即函数的“父母”)。
  • 执行统计:
    • 时间:这个功能花了多少时间,包括孩子们的时间。
    • 自己的时间:这个功能花了多少时间,不计算孩子们的时间。
    • 调用:调用函数的次数。

对于每个统计信息,您可以查看与整个分析会话相关的绝对值和百分比。

分析和跟踪允许我们调试性能问题。否则应用程序可能会正常工作,但找到性能问题的原因也是调试的一部分。

生产怎么样?

当您需要调试生产中发生的问题时,最安全的选择是在单元测试中重现它。这使您可以始终如一地连续运行本地计算机,修复问题,并确保问题仍然存在。

不幸的是,这并不总是可行的。有时,我们不知道问题是如何发生的。或者它可能是您的应用程序中发生的事情的组合,您无法轻松复制。无论如何,您可以更好地查看生产环境。

一种选择是在生产服务器上安装Xdebug。当我们提供具有正确值的请求参数或cookie时,只允许它启动。一定要保守这个价值

但是,Xdebug会对性能产生影响,更糟糕的是,可能会将您的应用程序打开以进行恶意攻击。这是监控发挥作用的地方。现代APM解决方案将监控正在运行的应用程序。这样,您可以在问题浮出水面后访问必要的信息。 Retrace支持PHP监控,包括类似的东西

  • 性能监控
  • 代码分析
  • 错误跟踪
  • 中心化记录
  • 指标

然后,您可以使用此信息作为起点来改进代码并修复本地计算机上的错误。

快乐的PHP调试

这篇文章为您提供了几种如何调试PHP应用程序的技巧。我们简要介绍了非交互式调试,但这是一种简单的调试方法。更好的方法是使用交互式路由并使用Xdebug。它为我们提供远程调试,逐步执行代码和变量检查。还有非常有用的跟踪和分析功能。确保在开发机器上执行此操作,而不是在生产中执行此操作。

错误将永远是任何软件的一部分。但是通过这些PHP调试技巧,您将能够更快,更轻松地理解和修复应用程序。

资讯来源:由0x资讯编译自STACKIFY,版权归作者Peter Morlion所有,未经许可,不得转载
你可能还喜欢