Delicious Bookmark this on Delicious Share on Facebook SlashdotSlashdot It! Digg! Digg



PHP : Function Reference : Error Handling and Logging Functions : debug_backtrace

debug_backtrace

Generates a backtrace (PHP 4 >= 4.3.0, PHP 5)
array debug_backtrace ( )

Example 590. debug_backtrace() example

<?php
// filename: a.php

function a_test($str)
{
   echo
"\nHi: $str";
   
var_dump(debug_backtrace());
}

a_test('friend');
?>

<?php
// filename: b.php
include_once '/tmp/a.php';
?>

Results similar to the following when executing /tmp/b.php:

Hi: friend
array(2) {
[0]=>
array(4) {
   ["file"] => string(10) "/tmp/a.php"
   ["line"] => int(10)
   ["function"] => string(6) "a_test"
   ["args"]=>
   array(1) {
     [0] => &string(6) "friend"
   }
}
[1]=>
array(4) {
   ["file"] => string(10) "/tmp/b.php"
   ["line"] => int(2)
   ["args"] =>
   array(1) {
     [0] => string(10) "/tmp/a.php"
   }
   ["function"] => string(12) "include_once"
 }
}

Code Examples / Notes » debug_backtrace

misterpib

You should probably try to avoid changing any of the items in the args array. Consider this example:
----------
function a(&$value)
{
 echo "start a: $value\n";
 b();
 echo "end a: $value\n";
}
function b()
{
 echo "start b\n";
 $stack = debug_backtrace();
 $stack[1]['args'][0] = 'bob';
 echo "end b\n";
}
$mynum = 42;
a($mynum);
--------------
This prints:
start a: 42
start b
end b
end a: bob


ad-rotator.com

To simply print out the file/function trace (chain of calls, file and line number before the error):
function getTrace() {
$vDebug = debug_backtrace();
$vFiles = array();
for ($i=0;$i<count($vDebug);$i++) {
  // skip the first one, since it's always this func
  if ($i==0) { continue; }
  $aFile = $vDebug[$i];
  $vFiles[] = '('.basename($aFile['file']).':'.$aFile['line'].')';
} // for
$vTraceStr = implode(',',$vFiles);
}


php

The value of the class argument has changed slightly between PHP4 and PHP5:
Here's an example:
<?php
 function get_class_static() {
   $bt = debug_backtrace();
   $name = $bt[1]['class'];
   return $name;
 }
 class foo {
   function printClassName() {
     print(get_class_static() . "\n");
    }
 }
 class bar extends foo {
 }
$f = new foo();
$b = new bar();
$f->printClassName();
$b->printClassName();
?>
In PHP4 you get:
 foo
 bar
In PHP5 you get:
 foo
 foo
debug_backtrace() now sets the 'class' parameter to be the class that the function is actually defined in, not the name of the instantiated class.


admin

Surprizingly, debug_backtrace() cannot aquire arguments from the function that is used as the second or later argument of a function.
<?php
function a($p) {
$backtrace = debug_backtrace();

if (isset($backtrace[0]['args']))
var_export($backtrace[0]['args']);
else
echo "Cannot aquire arguments";
echo "<br />";

return $p;
}
function b($p1, $p2, $p3) {
echo "$p1, $p2, $p3";
}
// This outputs:
//    array ( 0 => 'First a', )
//    Cannot aquire arguments
//    Cannot aquire arguments
//    First a, Second a, Third a
b(a("First a"), a("Second a"), a("Third a"));
?>


jlim#natsoft.com.my

Pretty print the backtrace(). Functions are indented based on call value, and file is linked using file:// for convenience.
Enjoy, John Lim
function adodb_backtrace($print=true)
{
$s = '';
if (PHPVERSION() >= 4.3) {

$MAXSTRLEN = 64;

$s = '<pre align=left>';
$traceArr = debug_backtrace();
array_shift($traceArr);
$tabs = sizeof($traceArr)-1;
foreach ($traceArr as $arr) {
for ($i=0; $i < $tabs; $i++) $s .= ' &nbsp; ';
$tabs -= 1;
$s .= '<font face="Courier New,Courier">';
if (isset($arr['class'])) $s .= $arr['class'].'.';
foreach($arr['args'] as $v) {
if (is_null($v)) $args[] = 'null';
else if (is_array($v)) $args[] = 'Array['.sizeof($v).']';
else if (is_object($v)) $args[] = 'Object:'.get_class($v);
else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
else {
$v = (string) @$v;
$str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
if (strlen($v) > $MAXSTRLEN) $str .= '...';
$args[] = $str;
}
}

$s .= $arr['function'].'('.implode(', ',$args).')';
$s .= sprintf("</font><font color=#808080 size=-1> # line %4d,".
 " file: <a href=\"file:/%s\">%s</a></font>",
 $arr['line'],$arr['file'],$arr['file']);
$s .= "\n";
}
$s .= '</pre>';
if ($print) print $s;
}
return $s;
}


diz

Ok as spagmoid already said, I just realized that my function has a similar bug than jlim's function.
So just add the following line:
if (is_array($bt['args']))
before line:
foreach ($bt['args'] as $a) {
This way you avoid the warning from being displayed.


http://synergy8.com

It should be noted that if an internal php function such as call_user_func in the backtrace, the 'file' and 'line' entries will not be set.
Most debug tracers will use these entries.  You should place a check to see if the key exists in the array before using this function.  Otherwise notices will be generated.
<?php
$arrTrace = debug_backtrace();
foreach ($arrTrace as $arr)
{
if (!isset ($arr['file']))
{
$arr['file'] = '[PHP Kernel]';
}
if (!isset ($arr['line']))
{
$arr['line'] = '';
}
// Do something
}
?>


jsnell

If you are using the backtrace function in an error handler, avoid using var_export() on the args, as you will cause fatal errors in some situations, preventing you from seeing your stack trace.  Some structures will cause PHP to generate the fatal error "Nesting level too deep - recursive dependency?" This is a design feature of php, not a bug (see http://bugs.php.net/bug.php?id=30471)

seaside dot ki

I've started creating an external debug server for PHP. A PHP app require_once's a TADebugger(), which communicates with the debug sever. Find the OS X universal binary here [PHP source sample included]:
  http://www.turingart.com/downloads/phpDebugger.zip
Currently, TADebugger allows to post these properties back to the debug server:
- Call backtraces
- String messages
- Source files, which were referenced by a backtrace call
Note, that the binary is a early version.


bernyregeling

I wrote this function, in addition to jlim, for a nice NO-HTML output.
Thee result has similarities to a Java-error. Hope you like it.
(BTW, this function exits the script too, if debug_backtrace is displayed)
------------------------------
function debug_bt()
{
if(!function_exists('debug_backtrace'))
{
echo 'function debug_backtrace does not exists'."\r\n";
return;
}
//echo '<pre>';
echo "\r\n".'----------------'."\r\n";
echo 'Debug backtrace:'."\r\n";
echo '----------------'."\r\n";
foreach(debug_backtrace() as $t)
{
echo "\t" . '@ ';
if(isset($t['file'])) echo basename($t['file']) . ':' . $t['line'];
else
{
// if file was not set, I assumed the functioncall
// was from PHP compiled source (ie XML-callbacks).
echo '<PHP inner-code>';
}
echo ' -- ';
if(isset($t['class'])) echo $t['class'] . $t['type'];
echo $t['function'];
if(isset($t['args']) && sizeof($t['args']) > 0) echo '(...)';
else echo '()';
echo "\r\n";
}
//echo '</pre>';
exit;
        }


tb

I use this for debugging in my object oriented systems.  It allows me to output a debug/error/warning function with exact information about the location that the error was thrown, which is useful.  Check it:
<?php
abstract class Debugger {

/**
* Throw a debug message.
*/
abstract function debug($msg);

/**
* Throw an error message.
*/
abstract function error($msg);

/**
* Throw a warning message.
*/
abstract function warning($msg);

/**
* Wrap a message with information about class, function, file and line
* number and return it.
*/
protected function getMsg($msg) {
$bt = debug_backtrace();

// get class, function called by caller of caller of caller
$class = $bt[2]['class'];
$function = $bt[2]['function'];

// get file, line where call to caller of caller was made
$file = $bt[1]['file'];
$line = $bt[1]['line'];

// build & return the message
return "$class::$function: $msg in $file at $line";
}

}
?>
Implement different debuggers for different scenarios (development, testing, production).  Each debugger extends Debugger; each of its methods (debug/error/warning) calls $this->getMsg($msg) to get a message with class, function, file, and line information.  Then it can either log it, email it, die with it, etc.
Then, just give each object (perhaps using a common superclass Object) a concrete debugger.  Then, from any object method, do something like:
<?php
class Foo extends Object {
    function bar() {
         $this->debugger->error("This is an error");
    }
}
?>
Which produces something like:
Foo::bar: This is an error in /some/file at X


fabian dot kraetzer

I coded a function, too. Just call debug() evertime you think you could encounter an error:
<?
   function debug()
   {
       $debug_array = debug_backtrace();
       $counter = count($debug_array);
       for($tmp_counter = 0; $tmp_counter != $counter; ++$tmp_counter)
       {
         ?>
         <table width="558" height="116" border="1" cellpadding="0" cellspacing="0" bordercolor="#000000">
           <tr>
             <td height="38" bgcolor="#D6D7FC"><font color="#000000">function <font color="#FF3300"><?
             echo($debug_array[$tmp_counter]["function"]);?>(</font> <font color="#2020F0"><?
             //count how many args a there
             $args_counter = count($debug_array[$tmp_counter]["args"]);
             //print them
             for($tmp_args_counter = 0; $tmp_args_counter != $args_counter; ++$tmp_args_counter)
             {
                echo($debug_array[$tmp_counter]["args"][$tmp_args_counter]);
                if(($tmp_args_counter + 1) != $args_counter)
                {
                  echo(", ");
                }
                else
                {
                  echo(" ");
                }
             }
             ?></font><font color="#FF3300">)</font></font></td>
           </tr>
           <tr>
             <td bgcolor="#5F72FA"><font color="#FFFFFF">{</font>
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file: <?
               echo($debug_array[$tmp_counter]["file"]);?></font>
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line: <?
               echo($debug_array[$tmp_counter]["line"]);?></font>
<font color="#FFFFFF">}</font></td>
           </tr>
         </table>
         <?
        if(($tmp_counter + 1) != $counter)
        {
          echo("
was called by:
");
        }
      }
       exit();
   }
?>


zmorris

Hi, I got tired of using a trace( $message, __FILE__, __LINE__ ) function I made.  It forced me to include the file and line params (since php doesn't have macros) so I decided to make an alternative.
Simply call this new version using trace( 'my message' ); and it prints out a stack trace in a clearer way than the one stored in the debug_backtrace() array.  It handles traces from outside of functions, traces in nested functions, and traces in included files, and also displays the function in a way that can be pasted right back into your php code for faster testing!
NOTE - be sure to save your files with the correct line endings for the line numbers to work correctly, which for Mac OS X is unix.  You can get to this option in the popup menu in the toolbar at the top of each window in BBEdit.
<?php
function    print_var( $var )
{
  if( is_string( $var ) )
      return( '"'.str_replace( array("\x00", "\x0a", "\x0d", "\x1a", "\x09"), array('\0', '\n', '\r', '\Z', '\t'), $var ).'"' );
  else if( is_bool( $var ) )
  {
      if( $var )
          return( 'true' );
      else
          return( 'false' );
  }
  else if( is_array( $var ) )
  {
      $result = 'array( ';
      $comma = '';
      foreach( $var as $key => $val )
      {
          $result .= $comma.print_var( $key ).' => '.print_var( $val );
          $comma = ', ';
      }
      $result .= ' )';
      return( $result );
  }
 
  return( var_export( $var, true ) );    // anything else, just let php try to print it
}
function    trace( $msg )
{
  echo "<pre>\n";
 
  //var_export( debug_backtrace() ); echo "</pre>\n"; return;    // this line shows what is going on underneath
 
  $trace = array_reverse( debug_backtrace() );
  $indent = '';
  $func = '';
 
  echo $msg."\n";
 
  foreach( $trace as $val)
  {
      echo $indent.$val['file'].' on line '.$val['line'];
     
      if( $func ) echo ' in function '.$func;
     
      if( $val['function'] == 'include' ||
          $val['function'] == 'require' ||
          $val['function'] == 'include_once' ||
          $val['function'] == 'require_once' )
          $func = '';
      else
      {
          $func = $val['function'].'(';
         
          if( isset( $val['args'][0] ) )
          {
              $func .= ' ';
              $comma = '';
              foreach( $val['args'] as $val )
              {
                  $func .= $comma.print_var( $val );
                  $comma = ', ';
              }
              $func .= ' ';
          }
         
          $func .= ')';
      }
     
      echo "\n";
     
      $indent .= "\t";
  }
 
  echo "</pre>\n";
}
trace( 'error outside function' );
function    test( $param1, $param2, $param3, $param4 )
{
  trace( 'error in test()' );
}
test( 1.1, "param2\n", array( 1 => "a\n", "b\n" => 2 ), false );
?>


php

Further to my previous note, the 'object' element of the array can be used to get the parent object.  So changing the get_class_static() function to the following will make the code behave as expected:
<?php
function get_class_static() {
$bt = debug_backtrace();

if (isset($bt[1]['object']))
return get_class($bt[1]['object']);
else
return $bt[1]['class'];
}
?>
HOWEVER, it still fails when being called statically.  Changing the last two lines of my previous example to
<?php
 foo::printClassName();
 bar::printClassName();
?>
...still gives the same problematic result in PHP5, but in this case the 'object' property is not set, so that technique is unavailable.


spagmoid

ATTN: jlim#natsoft.com.my
Great function, but you have a few bugs.
At the line:
foreach($arr['args'] as $v)
Change it to:
$args = array();
if(!empty($arr['args'])) foreach($arr['args'] as $v)
And since line & file are not present in the array if calling from the error handler,
$Line = (isset($arr['line'])? $arr['line'] : "unknown");
$File = (isset($arr['file'])? $arr['file'] : "unknown");
and substitute accordingly.
Here's my version of it, alas with different formatting:
----------------------------------------
function DBG_GetBacktrace()
{
$s = '';
$MAXSTRLEN = 64;

$s = '<pre align=left>';
$traceArr = debug_backtrace();
array_shift($traceArr);
$tabs = sizeof($traceArr)-1;
foreach($traceArr as $arr)
{
for ($i=0; $i < $tabs; $i++) $s .= ' &nbsp; ';
$tabs -= 1;
$s .= '<font face="Courier New,Courier">';
if (isset($arr['class'])) $s .= $arr['class'].'.';
$args = array();
if(!empty($arr['args'])) foreach($arr['args'] as $v)
{
if (is_null($v)) $args[] = 'null';
else if (is_array($v)) $args[] = 'Array['.sizeof($v).']';
else if (is_object($v)) $args[] = 'Object:'.get_class($v);
else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
else
{
$v = (string) @$v;
$str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
if (strlen($v) > $MAXSTRLEN) $str .= '...';
$args[] = "\"".$str."\"";
}
}
$s .= $arr['function'].'('.implode(', ',$args).')</font>';
$Line = (isset($arr['line'])? $arr['line'] : "unknown");
$File = (isset($arr['file'])? $arr['file'] : "unknown");
$s .= sprintf("<font color=#808080 size=-1> # line %4d, file: <a href=\"file:/%s\">%s</a></font>",
$Line, $File, $File);
$s .= "\n";
}    
$s .= '</pre>';
return $s;
}


tiwen

Another debug output. This is a short function that does not display the args (sometimes password are in arguments ...) and shows the callstack clearly in a table. In most cases i don't need more ...
<?php
function dieDebug($sError)
{
echo "<hr /><div>".$sError."<br /><table border='1'>";
$sOut=""; $aCallstack=debug_backtrace();

echo "<thead><tr><th>file</th><th>line</th><th>function</th>".
   "</tr></thead>";
foreach($aCallstack as $aCall)
{
if (!isset($aCall['file'])) $aCall['file'] = '[PHP Kernel]';
if (!isset($aCall['line'])) $aCall['line'] = '';
echo "<tr><td>{$aCall["file"]}</td><td>{$aCall["line"]}</td>".
   "<td>{$aCall["function"]}</td></tr>";
}
echo "</table></div><hr />";
die();
}
?>
To use it, simply do something like this:
<?php
if(...) dieDebug("another error found!");
?>


diz

And here are my two cents for a useful and good looking backtrace function.
<?php
function backtrace()
{
   $output = "<div style='text-align: left; font-family: monospace;'>\n";
   $output .= "<b>Backtrace:</b><br />\n";
   $backtrace = debug_backtrace();
   foreach ($backtrace as $bt) {
       $args = '';
       foreach ($bt['args'] as $a) {
           if (!empty($args)) {
               $args .= ', ';
           }
           switch (gettype($a)) {
           case 'integer':
           case 'double':
               $args .= $a;
               break;
           case 'string':
               $a = htmlspecialchars(substr($a, 0, 64)).((strlen($a) > 64) ? '...' : '');
               $args .= "\"$a\"";
               break;
           case 'array':
               $args .= 'Array('.count($a).')';
               break;
           case 'object':
               $args .= 'Object('.get_class($a).')';
               break;
           case 'resource':
               $args .= 'Resource('.strstr($a, '#').')';
               break;
           case 'boolean':
               $args .= $a ? 'True' : 'False';
               break;
           case 'NULL':
               $args .= 'Null';
               break;
           default:
               $args .= 'Unknown';
           }
       }
       $output .= "<br />\n";
       $output .= "<b>file:</b> {$bt['line']} - {$bt['file']}<br />\n";
       $output .= "<b>call:</b> {$bt['class']}{$bt['type']}{$bt['function']}($args)<br />\n";
   }
   $output .= "</div>\n";
   return $output;
}
?>
And here's a sample of how the output looks like (the last call is on the top):
Backtrace:
file: 56 - /tmp/test.php
call: backtrace()
file: 53 - /tmp/test.php
call: test->bar(15.4, Array(4))
file: 61 - /tmp/test.php
call: test->test("making new object", True)
file: 65 - /tmp/test.php
call: foo(Resource(#2), Null)


icefragment

A simple python-like backtrace. Note that I don't recurse into arrays if they are passed as arguments to functions.
function backtrace()
{
$bt = debug_backtrace();

echo("<br /><br />Backtrace (most recent call last):<br /><br />\n");
for($i = 0; $i <= count($bt) - 1; $i++)
{
if(!isset($bt[$i]["file"]))
echo("[PHP core called function]<br />");
else
echo("File: ".$bt[$i]["file"]."<br />");

if(isset($bt[$i]["line"]))
echo("&nbsp;&nbsp;&nbsp;&nbsp;line ".$bt[$i]["line"]."<br />");
echo("&nbsp;&nbsp;&nbsp;&nbsp;function called: ".$bt[$i]["function"]);

if($bt[$i]["args"])
{
echo("<br />&nbsp;&nbsp;&nbsp;&nbsp;args: ");
for($j = 0; $j <= count($bt[$i]["args"]) - 1; $j++)
{
if(is_array($bt[$i]["args"][$j]))
{
print_r($bt[$i]["args"][$j]);
}
else
echo($bt[$i]["args"][$j]);

if($j != count($bt[$i]["args"]) - 1)
echo(", ");
}
}
echo("<br /><br />");
}
}


kroczu

<?
// useful and comfortable debug function
// it's show memory usage and time flow between calls, so we can quickly find a block of code that need optimisation...
// example result:
/*
debug example.php> initialize
debug example.php> code-lines: 39-41 time: 2.0002 mem: 19 KB
debug example.php> code-lines: 41-44 time: 0.0000 mem: 19 KB
debug example.php> code-lines: 44-51 time: 0.6343 mem: 9117 KB
debug example.php> code-lines: 51-53 time: 0.1003 mem: 9117 KB
debug example.php> code-lines: 53-55 time: 0.0595 mem: 49 KB
*/
function debug()
{
  static $start_time = NULL;
  static $start_code_line = 0;
  $call_info = array_shift( debug_backtrace() );
  $code_line = $call_info['line'];
  $file = array_pop( explode('/', $call_info['file']));
  if( $start_time === NULL )
  {
      print "debug ".$file."> initialize\n";
      $start_time = time() + microtime();
      $start_code_line = $code_line;
      return 0;
  }
  printf("debug %s> code-lines: %d-%d time: %.4f mem: %d KB\n", $file, $start_code_line, $code_line, (time() + microtime() - $start_time), ceil( memory_get_usage()/1024));
  $start_time = time() + microtime();
  $start_code_line = $code_line;
}
////////////////////////////////////////////////
// example:
debug();
sleep(2);
debug();
// soft-code...
$a = 3 + 5;
debug();
// hard-code
for( $i=0; $i<100000; $i++)
{
   $dummy['alamakota'.$i] = 'alamakota'.$i;
}
debug();
usleep(100000);
debug();
unset($dummy);
debug();
?>


Change Language


Follow Navioo On Twitter
debug_backtrace
debug_print_backtrace
error_get_last
error_log
error_reporting
restore_error_handler
restore_exception_handler
set_error_handler
set_exception_handler
trigger_error
user_error
eXTReMe Tracker