您如何调试PHP脚本?[关闭]


403

您如何调试PHP脚本?

我知道诸如使用错误报告之类的基本调试。PHPEclipse中的断点调试也非常有用。

在phpStorm或任何其他IDE中进行调试的最佳方式(快速简便)是什么?




40
我相信这是一个很大的问题!当您不知道如何进行PHP调试时,您甚至都不知道如何表达您的问题,也不知道如何比这更精确。因此,它可能不遵守Stack的规则,但肯定对我们(初学者)有很大帮助!
Mihaela 2014年

1
从php5.4开始,引入了新的命令行界面调试器phpdbg(phpdbg.com)。PHP5.6将带有默认的phpdbg。
Ganesh Patil,2014年

1
听说过XDebug吗?:)
Pratik

Answers:


145

尝试使用Eclipse PDT来设置一个具有上述调试功能的Eclipse环境。与旧方法var_dump相比,调试代码的能力是一种更好的方法,它可以在各个点进行打印并在各个位置进行打印,以查看流向哪里出错。但是,当所有其他方法都失败了,而我所拥有的只是SSH和vim时,我仍然var_dump()/ die()找出代码在哪里。


35
您应该使用此函数:kill($ data){die(var_dump($ data)); }这样
可以省去


2
有没有办法美化“ var_dump”?
RPDeshaies 2014年

6
@ AlexMorley-Finch我带您去kill($data) { echo "<pre>"; var_dump($data); echo "</pre>"; exit; }
Francisco Presencia,2015年

2
链接可以通过令人难以置信的Web存档 “恢复” ,最后检查是从15年5月7日开始。
Gruber


38

这是我的小调试环境:

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;
        }
    }
}

2
谢谢。那挽救了我的一天。(我不得不删除E_STRICT)
二段

4
assert_callcack
Madbreaks 2015年

32

Xdebug和用于Notepad ++的DBGp插件用于繁重的错误查找,FirePHP用于轻量级的东西。又快又脏?没有什么比dBug更好了


DBGp插件不适用于当前版本的notepad ++ / xdebug,也没有计划对其进行修复。你可以看到我的讨论与联创作者在这里

26

XDebug对于开发至关重要。我先安装它,再安装其他任何扩展程序。它为您提供了有关任何错误的堆栈跟踪,并且您可以轻松启用分析。

为了快速查看数据结构,请使用var_dump()。不要使用print_r()它,因为您必须将它包围起来,<pre>并且一次只能打印一个变量。

<?php var_dump(__FILE__, __LINE__, $_REQUEST); ?>

对于真正的调试环境,我发现的最好的是Komodo IDE,但价格为$$。


19

PhpEd真的很好。您可以进入/超过/退出功能。您可以运行临时代码,检查变量,更改变量。太神奇了


4
与NetBeans或Eclipse等真正的IDE相比,我已经使用过PhpEd,但我对此并不客气,也没有对此问题添加任何有用的注释。-1
Siliconrockstar

在购买PhpED Professional之前,我尝试了大多数IDE(包括Zend,NetBeans和Eclipse),因为它是最好的一分半。这是几年前的事,所以其他的也许已经有所改善,但是当时大多数人都因为使用Java编写而非常缓慢。当我(对我而言)显然是最好的时,我不明白某人怎么会“不客气”,这个决定是不费吹灰之力的。
lm713

17

1)我使用print_r()。在TextMate中,我有一个'pre'的代码片段,它扩展为:

echo "<pre>";
print_r();
echo "</pre>";

2)我使用Xdebug,但无法在Mac上正常运行GUI。它至少打印出堆栈跟踪的可读版本。


我确定你的意思是echo“ </ pre>”; 最后。
altermativ

9
您也可以将“ true”传递给函数,以便它返回字符串。这意味着您可以执行以下操作:echo '<pre>', print_r($var, true), '</pre>';
DisgruntledGoat 2010年


16

老实说,print和print_r()的组合可以打印出变量。我知道许多人喜欢使用其他更高级的方法,但是我发现这是最容易使用的方法。

我要说的是,直到我在Uni进行了一些微处理器编程并且甚至不能使用它时,我才完全意识到这一点。


我很高兴您提到print和print_r,我使用基本的print来查看代码是否执行到了特定点,这有助于隔离问题。
Brad

我同时使用print和var_dump()。我使用print来显示调试消息和信息,并使用var_dump指示随着事情的进展变量的状态。
约书亚K

14

Xdebug的Derick Rethans的非常好。我前一段时间使用它,发现它并不是那么容易安装。一旦完成,您将不了解没有它的管理方式:-)

Zend Developer Zone上有一篇不错的文章(在Linux上安装似乎并不容易),甚至还有一个我从未使用过的Firefox插件


2
它不仅使安装令人沮丧。配置Xdebug与Eclipse配合使用可能是一场噩梦。我能够在CentOS 5上安装Xdebug,但是EclipsePDT + Xdebug不想合作:(
Jahangir


11

我将Netbeans与XDebug和Easy XDebug FireFox附件一起使用

在调试MVC项目时,该附件是必不可少的,因为XDebug在Netbeans中运行的正常方式是通过url注册dbug会话。在FireFox中安装了附加组件之后,您将设置Netbeans项目属性->运行配置->高级并选择“不打开Web浏览器”。现在,您可以像往常一样设置断点并使用Ctrl-F5开始调试会话。打开FireFox,然后右键单击右下角的“加载项”图标以开始监视断点。当代码到达断点时,它将停止,您可以检查变量状态和调用堆栈。



9

PhpEdit有一个内置的调试器,但是我通常最终使用echo();。和print_r(); 老式的方式!


8

对于确实很棘手的问题,使用print_r / echo来弄清时间太费时了,我使用我的IDE的(PhpEd)调试功能。与我使用过的其他IDE不同,PhpEd几乎不需要进行任何设置。我不使用它,因为我遇到任何问题,唯一的原因是,它是痛苦的缓慢。我不确定速度是否特定于PhpEd或任何php调试器。PhpEd不是免费的,但我相信它还是会使用一种开源调试器(如前面提到的XDebug)。同样,PhpEd的好处在于它不需要任何设置,而我在过去发现这非常繁琐。


2
PHPEd调试器实际上是由编写PHPEd的同一人编写的,我很确定它不是开源的。至少PHPEd不附带源代码,而是编译了.so和.dll。
Artem Russakovskii

4

对于我来说,手动调试通常更快一些,var_dump()并且debug_print_backtrace()是您使用逻辑进行调试所需的所有工具。


3

好吧,这在某种程度上取决于事情向南发展。这是我尝试隔离的第一件事,然后根据需要使用echo / print_r()。

注意:你们知道您可以将true作为第二个参数传递给print_r(),它将返回输出而不是打印输出?例如:

echo "<pre>".print_r($var, true)."</pre>";

2
我只是将其包装在一个称为debug的函数中。这样我就可以进行debug($ var);
jdelator

3

当无法使用Rails时,我经常使用CakePHP。为了调试错误,我通常error.log在tmp文件夹中找到,并在终端中使用以下命令将其尾巴...

tail -f app/tmp/logs/error.log

它可以让您从正在运行的蛋糕中运行对话框,这非常方便,如果您想在中间代码中向其输出内容,则可以使用。

$this->log('xxxx');

通常,这可以使您对发生的事情/出错的情况有个很好的了解。



2

Komodo IDE可以很好地与xdebug配合使用,甚至可以进行更多的调试。它需要最少的配置。您所需要的只是Komodo可以在本地使用的php版本,以在断点处逐步执行代码。如果您已将脚本导入到komodo项目中,则可以用鼠标单击设置断点,就像在eclipse内如何设置断点以调试Java程序一样。远程调试显然要使其正常工作(您可能必须在工作区中用php脚本映射远程URL)比本地调试设置更为棘手,如果您在MAC或linux桌面上,则该配置很容易配置。



2

有许多PHP调试技术可以在编码时节省大量时间。一种有效但基本的调试技术是仅打开错误报告。另一种更高级的技术涉及使用打印语句,它可以通过在屏幕上显示实际内容来帮助查明更多难以捉摸的错误。PHPeclipse是一个Eclipse插件,可以突出显示常见的语法错误,并且可以与调试器结合使用以设置断点。

display_errors = Off
error_reporting = E_ALL 
display_errors = On

并且也使用了

error_log();
console_log();


1

我使用带有内置调试器的zend Studio进行蚀。与使用xdebug用eclipse pdt进行调试相比,它仍然很慢。希望他们能解决这些问题,速度比最近的发行版有所提高,但处理问题仍需要2-3秒。zend firefox工具栏确实使事情变得容易(调试下一页,当前页面等)。它还提供了一个探查器,它将对您的代码进行基准测试,并提供饼图,执行时间等。


1

只需简单地发现大多数错误 var_dump输入一些关键变量,但这显然取决于您开发的应用程序类型。

对于更复杂的算法,步骤/断点/监视功能非常有用(如果没有必要)


1

PHP数据库

作为SAPI模块实现的交互式逐步调试PHP调试器,可以使您完全控制环境,而不会影响代码的功能或性能。它旨在成为适用于PHP 5.4+的轻量级,功能强大且易于使用的调试平台,并且随PHP 5.6一起提供。

功能包括:

  • 分步调试
  • 灵活的断点(类方法,函数,文件:行,地址,操作码)
  • 内置eval()轻松访问PHP
  • 轻松访问当前正在执行的代码
  • Userland API
  • 与SAPI无关-轻松集成
  • PHP配置文件支持
  • JIT Super Globals-自行设定!
  • 可选的readline支持-舒适的终端操作
  • 远程调试支持-捆绑的Java GUI
  • 操作简便

看截图:

PHP DBG-分步调试-屏幕截图

PHP DBG-分步调试-屏幕截图

主页:http//phpdbg.com/

PHP错误 -更好的PHP错误报告

这很容易使用库(实际上是一个文件)来调试PHP脚本。

您唯一需要做的就是添加一个文件,如下所示(在代码的开头):

require('php_error.php');
\php_error\reportErrors();

然后,所有错误都会为您提供信息,例如回溯,代码上下文,函数参数,服务器变量等。例如:

PHP错误|  改善PHP的错误报告-backtrace的屏幕截图 PHP错误|  改善PHP的错误报告-backtrace的屏幕截图 PHP错误|  改善PHP的错误报告-backtrace的屏幕截图

功能包括:

  • 琐碎的使用,只有一个文件
  • 浏览器中显示的针对正常请求和Ajaxy请求的错误
  • AJAX请求已暂停,使您可以自动重新运行它们
  • 使错误尽可能严格(鼓励代码质量,并倾向于提高性能)
  • 整个堆栈跟踪中的代码片段
  • 提供更多信息(例如全功能签名)
  • 修复了一些纯属错误的错误消息
  • 语法高亮
  • 看起来很漂亮!
  • 客制化
  • 手动打开和关闭
  • 运行特定部分而无错误报告
  • 忽略文件,从而避免在堆栈跟踪中突出显示代码
  • 申请文件;错误发生时,这些优先级高!

主页:http//phperror.net/

GitHub:https : //github.com/JosephLenton/PHP-Error

我的叉子(有额外的修复):https : //github.com/kenorb-contrib/PHP-Error

DTrace

如果您的系统支持DTrace动态跟踪(默认情况下安装在OS X上),并且PHP在启用DTrace探针--enable-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

这是简单的用法:

  1. 运行:sudo dtruss-php.d
  2. 在另一个终端上运行:php -r "phpinfo();"

为了测试这一点,您可以使用以下命令转到任何docroot index.php并运行PHP内置服务器:

php -S localhost:8080

之后,您可以访问该站点 http:// localhost:8080 /(或选择任何适合您的端口)。从那里访问一些页面以查看跟踪输出。

注意:默认情况下,Dtrace在OS X上可用,在Linux上,您可能需要dtrace4linux或检查其他替代方法

请参阅:在php.net上使用PHP和DTrace


系统点击

或者,通过安装SystemTap SDT开发包来检查SystemTap跟踪(例如 yum install systemtap-sdt-devel)来。

这是示例脚本(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

请参阅:使用了SystemTap与DTrace的PHP静电探测器在php.net



0

根据问题的不同,我喜欢将error_reporting(E_ALL)与回声测试混合使用(以查找违规的行/文件,错误是在最初发生的;您知道这并不总是行/文件php告诉您正确吗?),IDE大括号匹配(解决“解析错误:语法错误,意外的$ end”问题)和print_r(); 出口; 转储(真正的程序员查看源代码; p)。

您也无法通过“ memory_get_usage();”来击败phpdebug(检查sourceforge)。和“ memory_get_peak_usage();” 找到问题所在。


0

集成的调试器非常不错,您可以在其中逐步查看代码时观察变量的值。但是,它们确实需要在服务器上进行软件设置,并在客户端上进行一定数量的配置。两者都需要定期维护以保持良好的工作状态。

print_r易于编写,并保证可以在任何设置中工作。


0

通常,我发现创建一个自定义日志功能,该功能可以保存在文件中,存储调试信息,并最终在通用页脚上重新打印。

您还可以覆盖常见的Exception类,以便这种调试是半自动的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.