我怎样才能让PHP在出错时产生回溯?

cha*_*aos 49 php debugging error-handling stack-trace backtrace

尝试使用其默认的当前行错误消息来调试PHP是非常糟糕的.如何在生成错误时让PHP生成回溯(堆栈跟踪)?

cha*_*aos 47

我的脚本用于安装产生回溯的错误处理程序:

<?php
function process_error_backtrace($errno, $errstr, $errfile, $errline, $errcontext) {
    if(!(error_reporting() & $errno))
        return;
    switch($errno) {
    case E_WARNING      :
    case E_USER_WARNING :
    case E_STRICT       :
    case E_NOTICE       :
    case E_USER_NOTICE  :
        $type = 'warning';
        $fatal = false;
        break;
    default             :
        $type = 'fatal error';
        $fatal = true;
        break;
    }
    $trace = array_reverse(debug_backtrace());
    array_pop($trace);
    if(php_sapi_name() == 'cli') {
        echo 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
        foreach($trace as $item)
            echo '  ' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()' . "\n";
    } else {
        echo '<p class="error_backtrace">' . "\n";
        echo '  Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
        echo '  <ol>' . "\n";
        foreach($trace as $item)
            echo '    <li>' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()</li>' . "\n";
        echo '  </ol>' . "\n";
        echo '</p>' . "\n";
    }
    if(ini_get('log_errors')) {
        $items = array();
        foreach($trace as $item)
            $items[] = (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()';
        $message = 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ': ' . join(' | ', $items);
        error_log($message);
    }
    if($fatal)
        exit(1);
}

set_error_handler('process_error_backtrace');
?>
Run Code Online (Sandbox Code Playgroud)

警告:影响各种"PHP致命错误"是无能为力,因为Zend在他们的智慧中决定忽视这些错误set_error_handler().所以你仍然会得到无用的最终位置错误.

  • 使用register_shutdown_function()来发生致命错误 (9认同)
  • 由于 `debug_backtrace` 并没有超出关闭函数的范围(堆栈已经弹出),您可能有兴趣知道您仍然可以访问关闭函数中的全局范围变量。因此,找到发生“E_ERROR”的行并设置一个全局范围的变量,该变量等于前一行的“debug_backtrace”的内容。Viola,你有一个完整的回溯,从你的致命错误之前的那一行开始,它可以从`register_shutdown_function`定义的函数访问! (2认同)

pat*_*oll 45

Xdebug打印错误的回溯表,您不必编写任何PHP代码来实现它.

缺点是您必须将其安装为PHP扩展.

  • 我也绝对推荐Xdebug:如果没有它,我不会想象在PHP中开发!请注意,您绝对不应该将它安装在生产服务器上:它有点伤害性能(理论上,您不需要生产服务器中的那种信息) (8认同)

ken*_*orb 27

PHP错误

对于用PHP编写的PHP,这是更好的错误报告.无需额外的扩展!

使用浏览器中显示所有错误的正常AJAXy请求(处于暂停状态)是微不足道的.然后,所有错误都会在整个堆栈跟踪中为您提供回溯和代码上下文,包括函数参数,服务器变量.

您需要做的就是包含一个文件并调用函数(在代码的开头),例如

require('php_error.php');
\php_error\reportErrors();
Run Code Online (Sandbox Code Playgroud)

查看截图:

PHP错误|  改进PHP的错误报告 - 回溯的屏幕截图 PHP错误|  改进PHP的错误报告 - 回溯的屏幕截图 PHP错误|  改进PHP的错误报告 - 回溯的屏幕截图

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

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

调试PHP

一个完整的PHP调试器类,支持异常,错误,警报(来自用户),代码行和突出显示标志.

用法示例:

 <?php
        include( dirname(dirname(__FILE__))  . '/src/Debug.php' );
        //Catch all
        Debug::register();

        //Generate an errors
        if( this_function_does_not_exists( ) )
        {
            return false;
        }
    ?>
Run Code Online (Sandbox Code Playgroud)

PHP中的错误处理

下面的示例显示了通过触发错误并使用用户定义的函数处理错误来处理内部异常:

更短的方式(PHP):

<?php
function e($number, $msg, $file, $line, $vars) {
   print_r(debug_backtrace());
   die();
}
set_error_handler('e');
Run Code Online (Sandbox Code Playgroud)

更长的路(PHP):

// set to the user defined error handler
$old_error_handler = set_error_handler("myErrorHandler");

// error handler function
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
    if (!(error_reporting() & $errno)) {
        // This error code is not included in error_reporting
        return;
    }

    switch ($errno) {
    case E_USER_ERROR:
        echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
        echo "  Fatal error on line $errline in file $errfile";
        echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
        echo "Aborting...<br />\n";
        var_dump(debug_backtrace());
        exit(1);
        break;

    case E_USER_WARNING:
        echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
        break;

    case E_USER_NOTICE:
        echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
        break;

    default:
        echo "Unknown error type: [$errno] $errstr<br />\n";
        break;
    }

    /* Don't execute PHP internal error handler */
    return true;
}
Run Code Online (Sandbox Code Playgroud)

请参阅:http://www.php.net/manual/en/function.set-error-handler.php

注意:一次只能有一个错误异常.当您调用set_error_handler()函数时,它将返回旧错误处理程序的名称.您可以存储它并自己从错误处理程序中调用它 - 从而允许您拥有多个错误处理程序.


的XDebug

对于更高级的解决方案,您可以使用XDebug扩展PHP.

默认情况下,加载XDebug时,它应该在出现任何致命错误时自动显示回溯.或者您跟踪到文件(xdebug.auto_trace)以获得整个请求的非常大的回溯或进行性能分析(xdebug.profiler_enable)或其他设置.如果跟踪文件太大,您可以使用xdebug_start_trace()和xdebug_stop_trace()来转储部分跟踪.

安装

使用PECL:

pecl install xdebug
Run Code Online (Sandbox Code Playgroud)

在Linux上:

sudo apt-get install php5-xdebug
Run Code Online (Sandbox Code Playgroud)

在Mac上(使用Homebrew):

brew tap josegonzalez/php
brew search xdebug
php53-xdebug
Run Code Online (Sandbox Code Playgroud)

矿井配置示例:

[xdebug]

; Extensions
extension=xdebug.so
; zend_extension="/YOUR_PATH/php/extensions/no-debug-non-zts-20090626/xdebug.so"
; zend_extension="/Applications/MAMP/bin/php/php5.3.20/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so" ; MAMP

; Data
xdebug.show_exception_trace=1       ; bool: Show a stack trace whenever an exception is raised.
xdebug.collect_vars = 1             ; bool: Gather information about which variables are used in a certain scope.
xdebug.show_local_vars=1            ; int: Generate stack dumps in error situations.
xdebug.collect_assignments=1        ; bool: Controls whether Xdebug should add variable assignments to function traces.
xdebug.collect_params=4             ; int1-4: Collect the parameters passed to functions when a function call is recorded.
xdebug.collect_return=1             ; bool: Write the return value of function calls to the trace files.
xdebug.var_display_max_children=256 ; int: Amount of array children and object's properties are shown.
xdebug.var_display_max_data=1024    ; int: Max string length that is shown when variables are displayed.
xdebug.var_display_max_depth=3      ; int: How many nested levels of array/object elements are displayed.
xdebug.show_mem_delta=0             ; int: Show the difference in memory usage between function calls.

; Trace
xdebug.auto_trace=0                 ; bool: The tracing of function calls will be enabled just before the script is run.
xdebug.trace_output_dir="/var/log/xdebug" ; string: Directory where the tracing files will be written to.
xdebug.trace_output_name="%H%R-%s-%t"     ; string: Name of the file that is used to dump traces into.

; Profiler
xdebug.profiler_enable=0            ; bool: Profiler which creates files read by KCacheGrind.
xdebug.profiler_output_dir="/var/log/xdebug"  ; string: Directory where the profiler output will be written to.
xdebug.profiler_output_name="%H%R-%s-%t"      ; string: Name of the file that is used to dump traces into.
xdebug.profiler_append=0            ; bool: Files will not be overwritten when a new request would map to the same file.

; CLI
xdebug.cli_color=1                  ; bool: Color var_dumps and stack traces output when in CLI mode.

; Remote debugging
xdebug.remote_enable=off            ; bool: Try to contact a debug client which is listening on the host and port.
xdebug.remote_autostart=off         ; bool: Start a remote debugging session even GET/POST/COOKIE variable is not present.
xdebug.remote_handler=dbgp          ; select: php3/gdb/dbgp: The DBGp protocol is the only supported protocol.
xdebug.remote_host=localhost        ; string: Host/ip where the debug client is running.
xdebug.remote_port=9000             ; integer: The port to which Xdebug tries to connect on the remote host.
xdebug.remote_mode=req              ; select(req,jit): Selects when a debug connection is initiated.
xdebug.idekey="xdebug-cli"          ; string: IDE Key Xdebug which should pass on to the DBGp debugger handler.
xdebug.remote_log="/var/log/xdebug.log" ; string: Filename to a file to which all remote debugger communications are logged.
Run Code Online (Sandbox Code Playgroud)

Drupal 6和7

启用Devel时:

/**
 * Implements hook_watchdog().
 */
function foo_watchdog($log_entry) {
  if ($log_entry['type'] == 'php' && $log_entry['severity'] <= WATCHDOG_WARNING) {
    function_exists('dd') && dd(debug_backtrace());
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的函数会将每个错误的回溯记录到临时文件中(/tmp/drupal_debug.txt默认情况下).

或者通过以下方式找到文件:drush eval "echo file_directory_temp() . '/drupal_debug.txt'.

如果没有启用Devel,请使用旧学校方法:var_dump(debug_backtrace());而不是dd().


小智 7

我只是尝试在违规行设置一个包含debug_backtrace()内容的会话变量,然后使用register_shutdown_function()打印它.工作就像一个魅力.