views:

840

answers:

6

Trying to debug PHP using its default current-line-only error messages is horrible. How can I get PHP to produce a backtrace (stack trace) when errors are produced?

+4  A: 

My script for installing an error handler that produces a backtrace:

<?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');
?>

Caveat: it is powerless to affect various 'PHP Fatal Errors', since Zend in their wisdom decided that these would ignore set_error_handler(). So you still get useless final-location-only errors with those.

chaos
+1  A: 
$backtrace = debug_backtrace();

i wrote a little article about backtracing a while back

smoove666
+1  A: 

You can use debug_backtrace

Mythica
+8  A: 

Xdebug prints a backtrace table on errors, and you don't have to write any PHP code to implement it.

Downside is you have to install it as a PHP extension.

patcoll
I definitly recommend Xdebug too : I wouldn't imagine developping in PHP without it ! Just note that you should absolutly not install it on a production server : it's kinda hurting performances (and you don't, in theory, need that kind of information in a production server)
Pascal MARTIN
This just saved me a real headache, thanks patcoll
Marc Roberts
+2  A: 

As php debug extensions, there is Xdebug and PHP DBG. Each one has its advantages and disadvantages.

xav0989
+1  A: 

set_error_handler() + debug_backtrace()
( + debug_print_backtrace() in PHP5 )

Lukasz Czerwinski