关于eclipse:如何调试PHP脚本?

How do you debug PHP scripts?

如何调试PHP脚本?

我知道基本的调试方法,例如使用错误报告。 PHPEclipse中的断点调试也非常有用。

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


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


您可以在与javascript相同的环境中使用Firephp附加组件来调试Firebug,以调试php。

我也使用前面提到的Xdebug来分析php。


这是我的小调试环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 '[cc lang="php"]', print_r($e, true), '

';
出口;
}

函数shutdown_handler(){
尝试 {
如果(null!== $ error = error_get_last()){
抛出新的Customizable_Exception($ error ['message'],$ error ['type'],$ error ['file'],$ error ['line']);
}
} catch(Exception $ e){
exception_handler($ e);
}
}

类Customizable_Exception扩展了Exception {
公共功能__construct($ message = null,$ code = null,$ file = null,$ line = null){
如果($ code === null){
父母:: __ construct($ message);
} 别的 {
父母:: __ construct($ message,$ code);
}
如果($ file!== null){
$ this-> file = $ file;
}
如果($ line!== null){
$ this-> line = $ line;
}
}
}


Xdebug和用于记事本的DBGp插件用于重型bug搜索,FirePHP用于轻量级东西。又快又脏?没有什么比dBug。


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

要快速查看数据结构,请使用var_dump()。不要使用print_r(),因为您必须将其用<pre1>包围,并且一次只能打印一个var。

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

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


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


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

1
2
3
echo"[cc lang="php"]";
print_r();
echo"

";

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


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

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


我已经使用Zend Studio(5.5)和Zend Platform。这可以进行适当的调试,断点/单步执行代码等,尽管要付出一定的代价。


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

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


我将Netbeans与XDebug和Easy XDebug FireFox加载项一起使用

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


我将Netbeans与XDebug一起使用。
在其网站上查看有关如何配置它的文档。
http://php.netbeans.org/


如果您不想弄乱输出,则输出缓冲非常有用。我采用单行代码进行此操作,可以随意发表评论/取消评论

1
 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()是武装逻辑所需的所有工具。


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

将其结尾。

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

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

1
$this->log('xxxx');

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


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

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

1
echo"[cc lang="php"]".print_r($var, true)."

";


print_r(debug_backtrace());

或类似的东西:-)


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


Nusphere也是php的良好调试器


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

1
2
3
display_errors = Off
error_reporting = E_ALL
display_errors = On

,也用于

1
2
error_log();
console_log();

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


在生产环境中,我使用error_log()将相关数据记录到服务器的错误日志中。


PHP数据库

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

功能包括:

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

查看屏幕截图:

PHP

1
2
3
require('php_error.php');
\\php_error\
eportErrors();

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

PHP
PHP
PHP

功能包括:

  • 简单易用,只有一个文件
  • 浏览器中显示的针对正常请求和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脚本而无需时间:

1
2
sudo dtrace -qn 'php*:::function-entry { printf("%Y: PHP function-entry:\\t%s%s%s() in %s:%d\
", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2); }'

因此,已将以下别名添加到rc文件中(例如~/.bashrc~/.bash_aliases):

1
2
alias trace-php='sudo dtrace -qn"php*:::function-entry { printf("%Y: PHP function-entry:\\t%s%s%s() in %s:%d\
", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2); }"'

您可以使用易于记忆的别名来跟踪脚本:trace-php

这是更高级的dtrace脚本,只需将其保存到dtruss-php.d中,使其可执行(chmod +x dtruss-php.d)并运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/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)\
"
, walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1));
}

php*:::compile-file-return
{
    printf("%Y: PHP compile-file-return:\\t%s (%s)\
"
, walltimestamp, basename(copyinstr(arg0)), basename(copyinstr(arg1)));
}

php*:::error
{
    printf("%Y: PHP error message:\\t%s in %s:%d\
"
, walltimestamp, copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2);
}

php*:::exception-caught
{
    printf("%Y: PHP exception-caught:\\t%s\
"
, walltimestamp, copyinstr(arg0));
}

php*:::exception-thrown
{
    printf("%Y: PHP exception-thrown:\\t%s\
"
, walltimestamp, copyinstr(arg0));
}

php*:::execute-entry
{
    printf("%Y: PHP execute-entry:\\t%s:%d\
"
, walltimestamp, basename(copyinstr(arg0)), (int)arg1);
}

php*:::execute-return
{
    printf("%Y: PHP execute-return:\\t%s:%d\
"
, walltimestamp, basename(copyinstr(arg0)), (int)arg1);
}

php*:::function-entry
{
    printf("%Y: PHP function-entry:\\t%s%s%s() in %s:%d\
"
, 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\
"
, 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\
"
, walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1), copyinstr(arg2));
}

php*:::request-startup
{
    printf("%Y, PHP request-startup:\\t%s at %s via %s\
"
, walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1), copyinstr(arg2));
}

主页:GitHub上的dtruss-lamp

这是简单的用法:

  • 运行:sudo dtruss-php.d
  • 在另一个终端上运行:php -r"phpinfo();"
  • 要进行测试,可以使用index.php转到任何docroot并通过以下方式运行PHP内置服务器:

    1
    php -S localhost:8080

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

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

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

    系统点击

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

    这是示例脚本(all_probes.stp),用于在使用SystemTap的运行PHP脚本的整个过程中跟踪所有核心PHP静态探针点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    probe process("sapi/cli/php").provider("php").mark("compile__file__entry") {
        printf("Probe compile__file__entry\
    "
    );
        printf("  compile_file %s\
    "
    , user_string($arg1));
        printf("  compile_file_translated %s\
    "
    , user_string($arg2));
    }
    probe process("sapi/cli/php").provider("php").mark("compile__file__return") {
        printf("Probe compile__file__return\
    "
    );
        printf("  compile_file %s\
    "
    , user_string($arg1));
        printf("  compile_file_translated %s\
    "
    , user_string($arg2));
    }
    probe process("sapi/cli/php").provider("php").mark("error") {
        printf("Probe error\
    "
    );
        printf("  errormsg %s\
    "
    , user_string($arg1));
        printf("  request_file %s\
    "
    , user_string($arg2));
        printf("  lineno %d\
    "
    , $arg3);
    }
    probe process("sapi/cli/php").provider("php").mark("exception__caught") {
        printf("Probe exception__caught\
    "
    );
        printf("  classname %s\
    "
    , user_string($arg1));
    }
    probe process("sapi/cli/php").provider("php").mark("exception__thrown") {
        printf("Probe exception__thrown\
    "
    );
        printf("  classname %s\
    "
    , user_string($arg1));
    }
    probe process("sapi/cli/php").provider("php").mark("execute__entry") {
        printf("Probe execute__entry\
    "
    );
        printf("  request_file %s\
    "
    , user_string($arg1));
        printf("  lineno %d\
    "
    , $arg2);
    }
    probe process("sapi/cli/php").provider("php").mark("execute__return") {
        printf("Probe execute__return\
    "
    );
        printf("  request_file %s\
    "
    , user_string($arg1));
        printf("  lineno %d\
    "
    , $arg2);
    }
    probe process("sapi/cli/php").provider("php").mark("function__entry") {
        printf("Probe function__entry\
    "
    );
        printf("  function_name %s\
    "
    , user_string($arg1));
        printf("  request_file %s\
    "
    , user_string($arg2));
        printf("  lineno %d\
    "
    , $arg3);
        printf("  classname %s\
    "
    , user_string($arg4));
        printf("  scope %s\
    "
    , user_string($arg5));
    }
    probe process("sapi/cli/php").provider("php").mark("function__return") {
        printf("Probe function__return: %s\
    "
    , user_string($arg1));
        printf(" function_name %s\
    "
    , user_string($arg1));
        printf("  request_file %s\
    "
    , user_string($arg2));
        printf("  lineno %d\
    "
    , $arg3);
        printf("  classname %s\
    "
    , user_string($arg4));
        printf("  scope %s\
    "
    , user_string($arg5));
    }
    probe process("sapi/cli/php").provider("php").mark("request__shutdown") {
        printf("Probe request__shutdown\
    "
    );
        printf("  file %s\
    "
    , user_string($arg1));
        printf("  request_uri %s\
    "
    , user_string($arg2));
        printf("  request_method %s\
    "
    , user_string($arg3));
    }
    probe process("sapi/cli/php").provider("php").mark("request__startup") {
        printf("Probe request__startup\
    "
    );
        printf("  file %s\
    "
    , user_string($arg1));
        printf("  request_uri %s\
    "
    , user_string($arg2));
        printf("  request_method %s\
    "
    , user_string($arg3));
    }

    用法:

    1
    stap -c 'sapi/cli/php test.php' all_probes.stp

    请参阅:在php.net上将SystemTap与PHP DTrace静态探针配合使用


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

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


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

    您也无法通过" memory_get_usage();"来击败phpdebug(检查sourceforge)。和" memory_get_peak_usage();"查找问题区域。


    1用于print_r()。用它来转储对象或变量的内容。为了使其更具可读性,请使用pre标记,这样就无需查看源代码。

    1
    2
    echo '[cc lang="php"]';
    print_r($arrayOrObject);

    也var_dump($ thing)-这对于查看subthings的类型非常有用


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

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


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

    print_r易于编写,并且可以在任何设置中正常工作。


    推荐阅读