你如何调试PHP脚本?
我知道基本调试,例如使用错误报告。PHPEclipse中的断点调试也很有用。
在 phpStorm 或任何其他 IDE 中进行调试的最佳(就快速和简单而言)方法是什么?
尝试使用Eclipse PDT来设置具有您提到的调试功能的 Eclipse 环境。与旧方法 var_dump 相比,单步执行代码的能力是一种更好的调试方法,并在各个点打印以查看流程出错的地方。当所有其他方法都失败并且我所拥有的只是 SSH 和 vim 时,我仍然var_dump()
/die()
来查找代码往南的地方。
您可以使用 Firephp 插件来在与 javascript 相同的环境中调试 php。
我还使用前面提到的Xdebug来分析 php。
这是我的小调试环境:
error_reporting(-1);
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_BAIL, 0);
assert_options(ASSERT_QUIET_EVAL, 0);
assert_options(ASSERT_CALLBACK, 'assert_callcack');
set_error_handler('error_handler');
set_exception_handler('exception_handler');
register_shutdown_function('shutdown_handler');
function assert_callcack($file, $line, $message) {
throw new Customizable_Exception($message, null, $file, $line);
}
function error_handler($errno, $error, $file, $line, $vars) {
if ($errno === 0 || ($errno & error_reporting()) === 0) {
return;
}
throw new Customizable_Exception($error, $errno, $file, $line);
}
function exception_handler(Exception $e) {
// Do what ever!
echo '<pre>', print_r($e, true), '</pre>';
exit;
}
function shutdown_handler() {
try {
if (null !== $error = error_get_last()) {
throw new Customizable_Exception($error['message'], $error['type'], $error['file'], $error['line']);
}
} catch (Exception $e) {
exception_handler($e);
}
}
class Customizable_Exception extends Exception {
public function __construct($message = null, $code = null, $file = null, $line = null) {
if ($code === null) {
parent::__construct($message);
} else {
parent::__construct($message, $code);
}
if ($file !== null) {
$this->file = $file;
}
if ($line !== null) {
$this->line = $line;
}
}
}
Xdebug 和用于 Notepad++ 的 DBGp 插件用于重型错误搜索,FirePHP 用于轻量级的东西。又快又脏?没有什么比 dBug 更好的了。
XDebug对于开发是必不可少的。我在任何其他扩展之前安装它。它为您提供任何错误的堆栈跟踪,并且您可以轻松启用分析。
要快速查看数据结构,请使用var_dump()
. 不要使用print_r()
,因为你必须用它包围它,<pre>
它一次只打印一个 var。
<?php var_dump(__FILE__, __LINE__, $_REQUEST); ?>
对于真正的调试环境,我发现最好的是Komodo IDE,但它的成本是 $$。
PhpEd 真的很好。您可以进入/结束/退出功能。您可以运行临时代码、检查变量、更改变量。这是惊人的。
1) 我使用 print_r()。在 TextMate 中,我有一个“pre”片段,它扩展为:
echo "<pre>";
print_r();
echo "</pre>";
2) 我使用 Xdebug,但无法让 GUI 在我的 Mac 上正常工作。它至少打印出堆栈跟踪的可读版本。
我使用了Zend Studio (5.5)和Zend Platform。这提供了适当的调试、断点/跨代码等,尽管需要付出代价。
老实说,结合 print 和 print_r() 打印出变量。我知道许多人更喜欢使用其他更高级的方法,但我发现这是最容易使用的。
我会说,直到我在 Uni 做了一些微处理器编程并且甚至无法使用它之前,我才完全理解这一点。
Derick Rethans 的Xdebug非常好。我前段时间用过,发现安装起来不是那么容易。完成后,您将无法理解没有它的管理方式:-)
Zend Developer Zone上有一篇很好的文章(在 Linux 上安装似乎并不容易),甚至还有一个我从未使用过的Firefox 插件。
我将 Netbeans 与 XDebug 一起使用。在其网站上查看有关如何配置它的文档。 http://php.netbeans.org/
我将 Netbeans 与 XDebug 和Easy XDebug FireFox 附加组件一起使用
当您调试 MVC 项目时,插件是必不可少的,因为 XDebug 在 Netbeans 中运行的正常方式是通过 url 注册 dbug 会话。在 FireFox 中安装插件后,您将设置 Netbeans 项目属性 -> 运行配置 -> 高级并选择“不打开 Web 浏览器” 现在可以设置断点并像往常一样使用 Ctrl-F5 启动调试会话. 打开 FireFox 并右键单击右下角的 Add-on 图标以开始监视断点。当代码到达断点时,它将停止,您可以检查变量状态和调用堆栈。
如果您不想弄乱输出,输出缓冲非常有用。我在单行中执行此操作,我可以随意评论/取消评论
ob_start();var_dump(); user_error(ob_get_contents()); ob_get_clean();
PhpEdit 有一个内置调试器,但我通常最终使用 echo(); 和 print_r(); 老式的方式!!
对于真正棘手的问题,使用 print_r/echo 来弄清楚我使用我的 IDE (PhpEd) 调试功能太费时了。与我使用的其他 IDE 不同,PhpEd 几乎不需要任何设置。我不使用它来解决我遇到的任何问题的唯一原因是它非常缓慢。我不确定缓慢是否特定于 PhpEd 或任何 php 调试器。PhpEd 不是免费的,但我相信它无论如何都会使用其中一种开源调试器(如前面提到的 XDebug)。再一次,PhpEd 的好处是它不需要我过去发现非常乏味的设置。
手动调试对我来说通常更快 -var_dump()
并且debug_print_backtrace()
是您武装逻辑所需的所有工具。
嗯,在某种程度上,这取决于事情的发展方向。这是我尝试隔离的第一件事,然后我将根据需要使用 echo/print_r()。
注意:你们知道可以将 true 作为第二个参数传递给 print_r() 并且它会返回输出而不是打印它?例如:
echo "<pre>".print_r($var, true)."</pre>";
当无法使用 Rails 时,我经常使用 CakePHP。为了调试错误,我通常error.log
在 tmp 文件夹中找到并在终端中使用命令将其拖尾...
tail -f app/tmp/logs/error.log
它让你从正在发生的事情的蛋糕中运行对话框,这非常方便,如果你想向它输出一些可以使用的中间代码。
$this->log('xxxx');
这通常可以让您很好地了解发生了什么/错误。
print_r(debug_backtrace());
或类似的东西 :-)
Komodo IDE 可以很好地与 xdebug 配合使用,甚至可以进行更多调试。它需要最少的配置。您只需要一个 php 版本,Komodo 可以在本地使用它来单步执行断点上的代码。如果您将脚本导入 komodo 项目,那么您可以通过单击鼠标设置断点,就像在 eclipse 中设置它以调试 java 程序一样。与本地调试设置相比,远程调试显然更难让它正常工作(您可能必须在工作区中使用 php 脚本映射远程 URL),如果您在 MAC 或 Linux 桌面上,本地调试设置非常容易配置.
Nusphere 也是一个很好的 php nusphere调试器
有许多 PHP 调试技术可以在编码时为您节省无数时间。一种有效但基本的调试技术是简单地打开错误报告。另一种更高级的技术涉及使用打印语句,它可以通过在屏幕上显示实际发生的内容来帮助查明更难以捉摸的错误。PHPeclipse 是一个 Eclipse 插件,它可以突出常见的语法错误,并且可以与调试器一起使用来设置断点。
display_errors = Off
error_reporting = E_ALL
display_errors = On
并且还使用了
error_log();
console_log();
在生产环境中,我使用 error_log() 将相关数据记录到服务器的错误日志中。
我使用带有内置调试器的zend studio for eclipse。与使用 xdebug 的 eclipse pdt 进行调试相比,它仍然很慢。希望他们能解决这些问题,与最近的版本相比,速度有所提高,但仍然需要 2-3 秒。zend firefox 工具栏确实让事情变得简单(调试下一页、当前页面等)。它还提供了一个分析器,可以对您的代码进行基准测试并提供饼图、执行时间等。
大多数错误可以通过简单地设置var_dump
一些关键变量来轻松找到,但这显然取决于您开发的应用程序类型。
对于更复杂的算法,步进/断点/监视功能非常有用(如果没有必要)
Interactive Stepthrough PHP Debugger 作为 SAPI 模块实现,它可以让您完全控制环境,而不会影响代码的功能或性能。它旨在成为 PHP 5.4+ 的轻量级、功能强大、易于使用的调试平台,并且随 PHP 5.6 一起提供。
特点包括:
看截图:
主页: http: //phpdbg.com/
这是一个非常易于使用的库(实际上是一个文件)来调试您的 PHP 脚本。
您唯一需要做的就是包含一个如下文件(在代码的开头):
require('php_error.php');
\php_error\reportErrors();
然后所有错误都会为您提供回溯、代码上下文、函数参数、服务器变量等信息。例如:
特点包括:
主页: http: //phperror.net/
GitHub:https ://github.com/JosephLenton/PHP-Error
我的叉子(有额外的修复):https ://github.com/kenorb-contrib/PHP-Error
如果您的系统支持DTrace 动态跟踪(默认安装在 OS X 上)并且您的 PHP 编译时启用--enable-dtrace
了默认的 DTrace 探针( ),则此命令可以帮助您立即调试 PHP 脚本:
sudo dtrace -qn 'php*:::function-entry { printf("%Y: PHP function-entry:\t%s%s%s() in %s:%d\n", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2); }'
因此,鉴于以下别名已添加到您的rc文件中(例如~/.bashrc
, ~/.bash_aliases
):
alias trace-php='sudo dtrace -qn "php*:::function-entry { printf(\"%Y: PHP function-entry:\t%s%s%s() in %s:%d\n\", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2); }"'
您可以使用易于记住的别名来跟踪您的脚本:trace-php
。
这是更高级的 dtrace 脚本,只需将其保存到dtruss-php.d
中,使其可执行(chmod +x dtruss-php.d
)并运行:
#!/usr/sbin/dtrace -Zs
# See: https://github.com/kenorb/dtruss-lamp/blob/master/dtruss-php.d
#pragma D option quiet
php*:::compile-file-entry
{
printf("%Y: PHP compile-file-entry:\t%s (%s)\n", walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1));
}
php*:::compile-file-return
{
printf("%Y: PHP compile-file-return:\t%s (%s)\n", walltimestamp, basename(copyinstr(arg0)), basename(copyinstr(arg1)));
}
php*:::error
{
printf("%Y: PHP error message:\t%s in %s:%d\n", walltimestamp, copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2);
}
php*:::exception-caught
{
printf("%Y: PHP exception-caught:\t%s\n", walltimestamp, copyinstr(arg0));
}
php*:::exception-thrown
{
printf("%Y: PHP exception-thrown:\t%s\n", walltimestamp, copyinstr(arg0));
}
php*:::execute-entry
{
printf("%Y: PHP execute-entry:\t%s:%d\n", walltimestamp, basename(copyinstr(arg0)), (int)arg1);
}
php*:::execute-return
{
printf("%Y: PHP execute-return:\t%s:%d\n", walltimestamp, basename(copyinstr(arg0)), (int)arg1);
}
php*:::function-entry
{
printf("%Y: PHP function-entry:\t%s%s%s() in %s:%d\n", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2);
}
php*:::function-return
{
printf("%Y: PHP function-return:\t%s%s%s() in %s:%d\n", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2);
}
php*:::request-shutdown
{
printf("%Y: PHP request-shutdown:\t%s at %s via %s\n", walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1), copyinstr(arg2));
}
php*:::request-startup
{
printf("%Y, PHP request-startup:\t%s at %s via %s\n", walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1), copyinstr(arg2));
}
主页:GitHub 上的 dtruss-lamp
下面是简单的用法:
sudo dtruss-php.d
。php -r "phpinfo();"
.要对此进行测试,您可以使用以下方式访问任何 docrootindex.php
并运行 PHP 内置服务器:
php -S localhost:8080
之后,您可以通过http://localhost:8080/访问该站点(或选择您方便的任何端口)。从那里访问一些页面以查看跟踪输出。
注意:Dtrace 默认在 OS X 上可用,在 Linux 上您可能需要dtrace4linux或检查其他一些替代方案。
请参阅:在 php.net 上使用 PHP 和 DTrace
yum install systemtap-sdt-devel
或者,通过安装 SystemTap SDT 开发包(例如)来检查 SystemTap 跟踪。
下面是示例脚本 ( all_probes.stp
),用于在使用 SystemTap 运行 PHP 脚本期间跟踪所有核心 PHP 静态探测点:
probe process("sapi/cli/php").provider("php").mark("compile__file__entry") {
printf("Probe compile__file__entry\n");
printf(" compile_file %s\n", user_string($arg1));
printf(" compile_file_translated %s\n", user_string($arg2));
}
probe process("sapi/cli/php").provider("php").mark("compile__file__return") {
printf("Probe compile__file__return\n");
printf(" compile_file %s\n", user_string($arg1));
printf(" compile_file_translated %s\n", user_string($arg2));
}
probe process("sapi/cli/php").provider("php").mark("error") {
printf("Probe error\n");
printf(" errormsg %s\n", user_string($arg1));
printf(" request_file %s\n", user_string($arg2));
printf(" lineno %d\n", $arg3);
}
probe process("sapi/cli/php").provider("php").mark("exception__caught") {
printf("Probe exception__caught\n");
printf(" classname %s\n", user_string($arg1));
}
probe process("sapi/cli/php").provider("php").mark("exception__thrown") {
printf("Probe exception__thrown\n");
printf(" classname %s\n", user_string($arg1));
}
probe process("sapi/cli/php").provider("php").mark("execute__entry") {
printf("Probe execute__entry\n");
printf(" request_file %s\n", user_string($arg1));
printf(" lineno %d\n", $arg2);
}
probe process("sapi/cli/php").provider("php").mark("execute__return") {
printf("Probe execute__return\n");
printf(" request_file %s\n", user_string($arg1));
printf(" lineno %d\n", $arg2);
}
probe process("sapi/cli/php").provider("php").mark("function__entry") {
printf("Probe function__entry\n");
printf(" function_name %s\n", user_string($arg1));
printf(" request_file %s\n", user_string($arg2));
printf(" lineno %d\n", $arg3);
printf(" classname %s\n", user_string($arg4));
printf(" scope %s\n", user_string($arg5));
}
probe process("sapi/cli/php").provider("php").mark("function__return") {
printf("Probe function__return: %s\n", user_string($arg1));
printf(" function_name %s\n", user_string($arg1));
printf(" request_file %s\n", user_string($arg2));
printf(" lineno %d\n", $arg3);
printf(" classname %s\n", user_string($arg4));
printf(" scope %s\n", user_string($arg5));
}
probe process("sapi/cli/php").provider("php").mark("request__shutdown") {
printf("Probe request__shutdown\n");
printf(" file %s\n", user_string($arg1));
printf(" request_uri %s\n", user_string($arg2));
printf(" request_method %s\n", user_string($arg3));
}
probe process("sapi/cli/php").provider("php").mark("request__startup") {
printf("Probe request__startup\n");
printf(" file %s\n", user_string($arg1));
printf(" request_uri %s\n", user_string($arg2));
printf(" request_method %s\n", user_string($arg3));
}
用法:
stap -c 'sapi/cli/php test.php' all_probes.stp
请参阅:在 php.net上将 SystemTap 与 PHP DTrace 静态探针一起使用
+1 为 print_r()。使用它来转储对象或变量的内容。为了使其更具可读性,请使用 pre 标签,这样您就不需要查看源代码。
echo '<pre>';
print_r($arrayOrObject);
还有 var_dump($thing) - 这对于查看 subthings 的类型非常有用
根据问题,我喜欢将 error_reporting(E_ALL) 与 echo 测试结合使用(以查找最初发生错误的有问题的行/文件;您知道它并不总是 php 告诉您的行/文件对吗?),IDE 大括号匹配(解决“解析错误:语法错误,意外 $end”问题)和 print_r();出口; 转储(真正的程序员查看源代码;p)。
你也不能用“memory_get_usage();”打败 phpdebug(检查 sourceforge)和“memory_get_peak_usage();” 找到问题区域。
集成调试器,您可以在单步执行代码时观察变量更改的值,这真的很酷。但是,它们确实需要服务器上的软件设置和客户端上的一定数量的配置。两者都需要定期维护以保持良好的工作状态。
print_r 很容易编写,并且保证可以在任何设置中工作。
通常我发现创建一个自定义日志函数能够保存在文件中,存储调试信息,并最终在一个公共页脚上重新打印。
您还可以覆盖常见的异常类,以便这种类型的调试是半自动化的。