PHP Conference Japan 2024

debug_backtrace

(PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8)

debug_backtrace生成回溯跟踪

描述

debug_backtrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): array

debug_backtrace() 生成 PHP 回溯跟踪。

参数

options

此参数是以下选项的位掩码

debug_backtrace() 选项
DEBUG_BACKTRACE_PROVIDE_OBJECT 是否填充 "object" 索引。
DEBUG_BACKTRACE_IGNORE_ARGS 是否省略 "args" 索引,从而省略所有函数/方法参数以节省内存。

注意:

有四种可能的组合

debug_backtrace() 选项
debug_backtrace() 填充两个索引
debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)
debug_backtrace(1)
debug_backtrace(0) 省略索引 "object" 并填充索引 "args"
debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) 省略索引 "object" 和索引 "args"
debug_backtrace(2)
debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT|DEBUG_BACKTRACE_IGNORE_ARGS) 填充索引 "object" 并省略索引 "args"
debug_backtrace(3)

limit

此参数可用于限制返回的堆栈帧数。默认情况下 (limit=0) 它返回所有堆栈帧。

返回值

返回一个关联 array 数组。可能的返回元素如下所示

debug_backtrace() 可能返回的元素
名称 类型 描述
function string 当前函数名。另见 __FUNCTION__
line int 当前行号。另见 __LINE__
file string 当前文件名。另见 __FILE__
class string 当前 名。另见 __CLASS__
object object 当前 对象
type string 当前调用类型。如果是方法调用,则返回 "->"。如果是静态方法调用,则返回 "::"。如果是函数调用,则不返回任何内容。
args array 如果在函数内部,则列出函数参数。如果在包含文件中,则列出包含的文件名。

示例

示例 #1 debug_backtrace() 示例

<?php
// 文件名:/tmp/a.php

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

a_test('friend');
?>

<?php
// 文件名:/tmp/b.php
include_once '/tmp/a.php';
?>

执行 /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"
  }
}

参见

添加注释

用户贡献的注释 37 条注释

jurchiks101 at gmail dot com
11 年前
这里有一个我刚刚编写的函数,用于获取清晰易懂的调用跟踪。它可能比其他一些替代方案更消耗资源,但它简短易懂,并提供良好的输出 (Exception->getTraceAsString())。

<?php
function generateCallTrace()
{
$e = new Exception();
$trace = explode("\n", $e->getTraceAsString());
// 反转数组以使步骤按时间顺序排列
$trace = array_reverse($trace);
array_shift($trace); // 删除 {main}
array_pop($trace); // 删除对该方法的调用
$length = count($trace);
$result = array();

for (
$i = 0; $i < $length; $i++)
{
$result[] = ($i + 1) . ')' . substr($trace[$i], strpos($trace[$i], ' ')); // 将 '#someNum' 替换为 '$i)',设置正确的顺序
}

return
"\t" . implode("\n\t", $result);
}
?>

示例输出
1) /var/www/test/test.php(15): SomeClass->__construct()
2) /var/www/test/SomeClass.class.php(36): SomeClass->callSomething()
robert at medianis dot net
8 年前
关于 PHP 5.3.6 或更高版本的 debug_backtrace 选项的简短说明

debug_backtrace() - 显示所有选项
debug_backtrace(0) - 排除 ["object"]
debug_backtrace(1) - 与 debug_backtrace() 相同
debug_backtrace(2) - 排除 ["object"] 和 ["args"]

使用此示例并尝试使用不同的选项调用 debug_backtrace

<?php
function F1()
{
echo
"<br />";
echo
"in F1 now";
echo
"<pre>".print_r(debug_backtrace(2),true)."</pre>";
}

class
DebugOptionsTest
{
function
F2()
{
echo
"<br />";
echo
"in F2 now";
F1();
}

}

echo
"<hr />calling F1";
F1();

$c=new DebugOptionsTest();
echo
"<hr /><hr /><hr />calling F2";
$c->F2("testValue");

?>
jsnell at e-normous dot com
17年前
如果您在错误处理程序中使用回溯函数,请避免对参数使用 var_export(),因为在某些情况下这会造成致命错误,阻止您查看堆栈跟踪。某些结构会导致 PHP 生成致命错误“嵌套级别太深 - 递归依赖?”。这是 php 的设计特性,而不是 bug(参见 http://bugs.php.net/bug.php?id=30471
liam at N0SPAM dot boxclever dot ca
1年前
位掩码参数提供的选项可以使用 ! 禁用。

<?php
debug_backtrace
( !DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
?>
Emmett Brosnan
8 年前
来自 debug_backtrace 的快速简易格式化输出。

$file_paths = debug_backtrace();

foreach($file_paths AS $file_path) {
foreach($file_path AS $key => $var) {
if($key == 'args') {
foreach($var AS $key_arg => $var_arg) {
echo $key_arg . ': ' . $var_arg . '<br>';
}
} else {
echo $key . ': ' . $var . '<br>';
}
}
}
michael dot schramm at gmail dot com
15年前
如果您使用对象作为函数调用的参数,请小心!

<?php
error_reporting
(E_ALL);

function
myPrint($trace){
foreach(
$trace as $i=>$call){
/**
* 这段代码是必须的!如果所有对象都有 __toString 函数,则不需要!
*
* Catchable fatal error: Object of class B could not be converted to string
* Catchable fatal error: Object of class A could not be converted to string
* Catchable fatal error: Object of class B could not be converted to string
*/
if (is_object($call['object'])) { $call['object'] = 'CONVERTED OBJECT OF CLASS '.get_class($call['object']); }
if (
is_array($call['args'])) {
foreach (
$call['args'] AS &$arg) {
if (
is_object($arg)) { $arg = 'CONVERTED OBJECT OF CLASS '.get_class($arg); }
}
}

$trace_text[$i] = "#".$i." ".$call['file'].'('.$call['line'].') ';
$trace_text[$i].= (!empty($call['object'])?$call['object'].$call['type']:'');
$trace_text[$i].= $call['function'].'('.implode(', ',$call['args']).')';
}

var_dump($trace_text);
}

class
A{
public function
test($obj){
$obj->test();
}
}

class
B{
public function
test(){
echo
myPrint(debug_backtrace());
}
}

$A = new A();
$B = new B();

$A->test($B);

?>
http://synergy8.com
18年前
需要注意的是,如果回溯中包含诸如 call_user_func 之类的内部 php 函数,“file”和“line”条目将不会设置。

大多数调试跟踪器都会使用这些条目。在使用此函数之前,您应该进行检查以查看数组中是否存在此键。否则会生成通知。

<?php

$arrTrace
= debug_backtrace();

foreach (
$arrTrace as $arr)
{
if (!isset (
$arr['file']))
{
$arr['file'] = '[PHP Kernel]';
}

if (!isset (
$arr['line']))
{
$arr['line'] = '';
}

// 执行某些操作
}

?>
root at jackyyf dot com
11 年前
使用 register_shutdown_function 时,并在关闭时调用该函数,则没有关于此函数的行号或文件名信息,仅提供函数、类(如果可能)、类型(如果可能)和参数。
Bill Getas
13年前
这是我的一点更新贡献 - 它以我喜欢的形式打印彩色输出。定义一个包含您的 IP 的数组的辅助函数 isRootIp();然后对 bt() 的调用简单地返回,因此您可以在任何人都不知道的情况下在活动站点中添加回溯。

<?php
function bt()
{
if( !
isRootIp() )
{
return
false;
}
array_walk( debug_backtrace(), create_function( '$a,$b', 'print "<br /><b>". basename( $a[\'file\'] ). "</b> &nbsp; <font color=\"red\">{$a[\'line\']}</font> &nbsp; <font color=\"green\">{$a[\'function\']} ()</font> &nbsp; -- ". dirname( $a[\'file\'] ). "/";' ) );
}
?>
匿名
11 年前
一个简单的函数,用于获取“文件名:[类->][函数(): ]”形式的字符串。

<?php
函数 get_caller_info() {
$c = '';
$file = '';
$func = '';
$class = '';
$trace = debug_backtrace();
if (isset(
$trace[2])) {
$file = $trace[1]['file'];
$func = $trace[2]['function'];
if ((
substr($func, 0, 7) == 'include') || (substr($func, 0, 7) == 'require')) {
$func = '';
}
} else if (isset(
$trace[1])) {
$file = $trace[1]['file'];
$func = '';
}
if (isset(
$trace[3]['class'])) {
$class = $trace[3]['class'];
$func = $trace[3]['function'];
$file = $trace[2]['file'];
} else if (isset(
$trace[2]['class'])) {
$class = $trace[2]['class'];
$func = $trace[2]['function'];
$file = $trace[1]['file'];
}
if (
$file != '') $file = basename($file);
$c = $file . ": ";
$c .= ($class != '') ? ":" . $class . "->" : "";
$c .= ($func != '') ? $func . "(): " : "";
return(
$c);
}
?>

用法示例

<?php
函数 debug($str) {
echo
get_caller_info() . $str . "<br>\n";
}
?>

get_caller_info() 函数将返回调用 debug() 函数的函数/类->方法的信息。
jonas at faceways dot se
11 年前
当使用 debug_backtrace() 检查是否从另一个调用者访问时,请记住要求 debug_backtrace() 只深入到所需的深度,并跳过将整个调试对象作为返回参数。

<?php
if (count(debug_backtrace(FALSE, 1)) == 0)
{
// 执行某些操作
}
?>
anoam at yandex dot ru
10年前
在不同的PHP版本中,它对资源的处理方式略有不同。

例如
函数 foo($bar)
{
返回 debug_backtrace();
}

$resource = fopen(__FILE__, 'r');
$backtrace = foo($resource);
echo "资源打开时: " . gettype($backtrace[0]['args'][0]) . "\n";
fclose($resource);
echo "资源关闭时: " . gettype($backtrace[0]['args'][0]) . "\n";

在5.3.10版本中,我得到的结果是:
资源打开时: resource
资源关闭时: resource

在5.5.9版本中:
资源打开时: resource
资源关闭时: unknown type

请注意。
d at rren dot me
12年前
大家好,只是一个提示 - 结果数组中的 ['args'] 数据是通过引用提供的。我发现自己不知不觉地修改了引用,如果您多次调用 backtrace,这会在后面产生问题。

<?php
$trace
= array_reverse(debug_backtrace());

// 循环遍历回溯信息
$la = 0;
$lb = count($trace);
while (
$la<$lb){

// 来自回溯的数据
$trace[$la]['file'];
$trace[$la]['line'];
$trace[$la]['args'];
$trace[$la]['function'];
// 来自回溯的数据

// 循环遍历参数数组
$ba = 0;
$bb = count($trace[$la]['args']);
while (
$ba<$bb){

$trace[$la]['args'][$ba] = "正在修改引用/指针";

$ba++;
}
unset(
$bb);
unset(
$ba);
// 循环遍历参数数组

$la++;
}
unset(
$lb);
unset(
$la);
// 循环遍历回溯信息
?>
kroczu AT interia DOT pl
17年前
<?
// 有用且方便的调试函数
// 它显示内存使用情况和调用之间的时间流逝,因此我们可以快速找到需要优化的代码块……
// 示例结果
/*
debug example.php> 初始化
debug example.php> 代码行:39-41 时间:2.0002 内存:19 KB
debug example.php> 代码行:41-44 时间:0.0000 内存:19 KB
debug example.php> 代码行:44-51 时间:0.6343 内存:9117 KB
debug example.php> 代码行:51-53 时间:0.1003 内存:9117 KB
debug example.php> 代码行:53-55 时间:0.0595 内存:49 KB
*/

函数 debug()
{
静态 $start_time = NULL;
静态 $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."> 初始化\n";
$start_time = time() + microtime();
$start_code_line = $code_line;
返回 0;
}

printf("debug %s> 代码行:%d-%d 时间:%.4f 内存:%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;
}

////////////////////////////////////////////////
// 示例

debug();
sleep(2);
debug();
// 软代码……
$a = 3 + 5;
debug();

// 硬代码
for( $i=0; $i<100000; $i++)
{
$dummy['alamakota'.$i] = 'alamakota'.$i;
}
debug();
usleep(100000);
debug();
unset($dummy);
debug();

?>
seaside dot ki at mac dot com
17年前
我开始为PHP创建一个外部调试服务器。一个PHP应用程序需要 `require_once` 一个 TADebugger(),它与调试服务器通信。可以在此处找到OS X通用二进制文件[包含PHP源代码示例]

http://www.turingart.com/downloads/phpDebugger.zip

目前,TADebugger允许将这些属性发布回调试服务器

- 调用回溯
- 字符串消息
- 由回溯调用引用的源文件

请注意,该二进制文件是早期版本。
admin at sgssweb dot com
18年前
令人惊讶的是,debug_backtrace() 无法获取用作函数的第二个或后续参数的函数的参数。

<?php

function a($p) {
$backtrace = debug_backtrace();

if (isset(
$backtrace[0]['args']))
var_export($backtrace[0]['args']);
else
echo
"无法获取参数";
echo
"<br />";

return
$p;
}

function
b($p1, $p2, $p3) {
echo
"$p1, $p2, $p3";
}

// 输出结果如下:
// array ( 0 => 'First a', )
// 无法获取参数
// 无法获取参数
// First a, Second a, Third a
b(a("First a"), a("Second a"), a("Third a"));

?>
kenorb at gmail dot com
14年前
一行代码打印最简洁易读的回溯信息:)
<?php
array_walk
(debug_backtrace(),create_function('$a,$b','print "{$a[\'function\']}()(".basename($a[\'file\']).":{$a[\'line\']}); ";'));
?>
jcmargentina at gmail dot com
5年前
我想指出的是,新版本的PHP中的`debug_backtrace()`可以检测递归/循环引用,避免内存消耗。

示例

<?php

class ParentClass {
public function
__construct()
{
$this->_child = new ChildClass($this);
var_dump(debug_backtrace());
}
}

class
ChildClass {
public function
__construct(ParentClass $p)
{
$this->_parent = $p;
}
}

$test = new ParentClass();
?>

输出

array(1) {
[0]=>
array(7) {
["file"]=>
string(23) "/home/jcm/testdebug.php"
["line"]=>
int(18)
["function"]=>
string(11) "__construct"
["class"]=>
string(11) "ParentClass"
["object"]=>
object(ParentClass)#1 (1) {
["_child"]=>
object(ChildClass)#2 (1) {
["_parent"]=>
*RECURSION*
}
}
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
}

请注意提供的*RECURSION*提示
jake at qzdesign dot co dot uk
5年前
`args` 元素只包含实际传递给函数或方法的参数。如果默认参数没有显式指定,它不包含默认参数。(至少在 PHP 7.1.9 中是这样)。这与 `func_get_args()` 的行为一致。
匿名用户
11 年前
一个典型的条目如下所示
<?php
array(6) {
'file' =>
string(87) "DbSelector.php"
'line'
=>
int(171)
'function' =>
string(5) "error"
'class'
=>
string(42) "LoggingService"
'type'
=>
string(2) "::"
'args'
=>
array(
1) {
[
0] =>
string(27) "Connecting to DB: unittests"
}
}
?>

但是要注意的是,'file' 和 'class' 指代的不是同一事物!
'file' 表示哪个文件调用了下一步。
'class' 是被调用的下一步。

所以 'file' 是调用者,'class' 是被调用者。
jlammertink at gmail dot com
14年前
我使用这个简单但有效的函数,这样我就可以看到子类中的哪个方法调用了当前方法(在父类中)。

<?php
function get_caller_method()
{
$traces = debug_backtrace();

if (isset(
$traces[2]))
{
return
$traces[2]['function'];
}

return
null;
}
?>
nyoung55 at that_google_mail.com
11 年前
这是一个将`debug_backtrace`结果清晰地输出到`error_log`的函数。

<?php
/*
* 将回溯输出发送到 error_log
* @param string $message 可选消息,将在回溯之前发送到 error_log
*/
function log_trace($message = '') {
$trace = debug_backtrace();
if (
$message) {
error_log($message);
}
$caller = array_shift($trace);
$function_name = $caller['function'];
error_log(sprintf('%s: Called from %s:%s', $function_name, $caller['file'], $caller['line']));
foreach (
$trace as $entry_id => $entry) {
$entry['file'] = $entry['file'] ? : '-';
$entry['line'] = $entry['line'] ? : '-';
if (empty(
$entry['class'])) {
error_log(sprintf('%s %3s. %s() %s:%s', $function_name, $entry_id + 1, $entry['function'], $entry['file'], $entry['line']));
} else {
error_log(sprintf('%s %3s. %s->%s() %s:%s', $function_name, $entry_id + 1, $entry['class'], $entry['function'], $entry['file'], $entry['line']));
}
}
}
?>
Gemorroj
11 年前
另一个格式化回溯的变体。
参数`$ignore`用于忽略额外的调用。
<?php
/**
* 获取调用堆栈
*
* @param int $ignore 忽略调用次数
*
* @return string
*/
protected function getBacktrace($ignore = 2)
{
$trace = '';
foreach (
debug_backtrace() as $k => $v) {
if (
$k < $ignore) {
continue;
}

array_walk($v['args'], function (&$item, $key) {
$item = var_export($item, true);
});

$trace .= '#' . ($k - $ignore) . ' ' . $v['file'] . '(' . $v['line'] . '): ' . (isset($v['class']) ? $v['class'] . '->' : '') . $v['function'] . '(' . implode(', ', $v['args']) . ')' . "\n";
}

return
$trace;
}
?>
kexianbin at diyism dot com
12年前
无需服务器上安装Xdebug或dbg.so,返回更详细的消息

diyism_trace.php
<?php
define
(TRACES_MODE, 'TEXTAREA');//'TEXTAREA' 或 'FIREPHP'
$GLOBALS['traces.pre']=array();
function
my_array_diff($arr1, $arr2)
{foreach (
$arr1 as $k=>$v)
{if (
in_array($v, $arr2, true))
{unset(
$arr1[$k]);
}
}
return
$arr1;
}
function
my_var_export($var, $is_str=false)
{
$rtn=preg_replace(array('/Array\s+\(/', '/\[(\d+)\] => (.*)\n/', '/\[([^\d].*)\] => (.*)\n/'), array('array (', '\1 => \'\2\''."\n", '\'\1\' => \'\2\''."\n"), substr(print_r($var, true), 0, -1));
$rtn=strtr($rtn, array("=> 'array ('"=>'=> array ('));
$rtn=strtr($rtn, array(")\n\n"=>")\n"));
$rtn=strtr($rtn, array("'\n"=>"',\n", ")\n"=>"),\n"));
$rtn=preg_replace(array('/\n +/e'), array('strtr(\'\0\', array(\' \'=>\' \'))'), $rtn);
$rtn=strtr($rtn, array(" Object',"=>" Object'<-"));
if (
$is_str)
{return
$rtn;
}
else
{echo
$rtn;
}
}
function
tick_handler()
{
$tmp=debug_backtrace();
$trace=my_array_diff($tmp, $GLOBALS['traces.pre']);
//echo '<pre>';var_export($trace);echo '</pre>';echo '<br/>'; //用于调试 diyism_trace.php
$trace=array_values($trace);
$GLOBALS['traces.pre']=$tmp;
if (
count($trace)>0 && $trace[0]['file'].'/'.@$tmp[1]['function']!==@$GLOBALS['traces'][count($GLOBALS['traces'])-1]['key']) //过滤空数组并重新排列array_values(),因为某些行每行会触发两次tick事件,例如:1.最后一行是“some code;questmark>” 2.error_reporting(...
{for ($i=count($trace)-1; $i>=0; --$i)
{
$GLOBALS['traces'][]=$tmp_fb=array_merge(array('key'=>$trace[$i]['file'].'/'.@$tmp[$i+1]['function']), $trace[$i], array('function'=>strtr($trace[$i]['function'], array('tick_handler'=>'CONTINUE')), 'in_function'=>@$tmp[$i+1]['function']));
TRACES_MODE==='FIREPHP'?fb(trace_output($tmp_fb), 'diyism_trace:'.++$GLOBALS['diyism_trace_no']):'';
}
}
}
function
trace_output($trace)
{
$trace['in_function']=strtr(@$trace['in_function'], array('require'=>'', 'require_once'=>'', 'include'=>'', 'include_once'=>''));
$trace['args']=$trace['args']?strtr(preg_replace(array('/\n +/'), array(''), preg_replace(array('/\n \d+ => /'), array(''), substr(my_var_export($trace['args'], true), 7, -3))), array("\r"=>'\r', "\n"=>'\n')):'';
return
$trace['file'].($trace['in_function']?'/'.$trace['in_function'].'()':'').'/'.$trace['line'].': '.$trace['function'].'('.$trace['args'].')';
}
function
traces_output()
{echo
'<textarea style="width:100%;height:300px;">';
$GLOBALS['traces']=array_slice($GLOBALS['traces'], 2);//移除注册tick行和引入'diyism_trace.php'行
foreach ($GLOBALS['traces'] as $k=>$trace)
{echo
htmlentities($k.':'.trace_output($trace)."\n");
}
echo
'</textarea>';
}
register_tick_function('tick_handler');
TRACES_MODE==='TEXTAREA'?register_shutdown_function('traces_output'):'';
?>

test.php
<?php
declare(ticks=1);
require
'diyism_trace.php';

a('a', array('hello'));
1+2;
b();
function
a()
{
$d=1;
b();
$d=2;
}
function
b()
{
1+1;
}
?>
php 新手
14年前
令人惊讶的是,没有人描述过它最好的用途之一:转储变量并显示位置。在调试过程中,尤其是在大型且不熟悉的系统中,记住我在哪里添加了这些 var_dump 非常痛苦。此外,这种方式在多个转储调用之间有一个分隔符。

<?php

function dump( $var ) {
$result = var_export( $var, true );
$loc = whereCalled();
return
"\n<pre>Dump: $loc\n$result</pre>";
}

function
whereCalled( $level = 1 ) {
$trace = debug_backtrace();
$file = $trace[$level]['file'];
$line = $trace[$level]['line'];
$object = $trace[$level]['object'];
if (
is_object($object)) { $object = get_class($object); }

return
"调用位置:第 $line 行,$object 类\n(位于 $file)";
}
?>

此外,从任何函数调用 `whereCalled()` 都可以快速识别出执行意外操作的位置(例如,在错误的时间更新属性)。我刚开始学习PHP,但在过去几年里一直在使用Perl中的等效函数。
php at kennel17 dot co dot uk
17年前
关于我之前的说明,数组的“object”元素可用于获取父对象。因此,将 `get_class_static()` 函数更改为以下内容将使代码按预期运行

<?php
function get_class_static() {
$bt = debug_backtrace();

if (isset(
$bt[1]['object']))
return
get_class($bt[1]['object']);
else
return
$bt[1]['class'];
}
?>

然而,当静态调用时,它仍然会失败。将我之前示例的最后两行更改为

<?php
foo
::printClassName();
bar::printClassName();
?>

…在PHP5中仍然会产生相同的问题结果,但在这种情况下,“object”属性未设置,因此该技术不可用。
john dot risken at gmail dot com
14年前
每个人似乎都有自己喜欢的用法。我用这个函数替换了 `die()`。它向用户显示一条消息
并向我发送导致错误的 `PrettyPrint` 邮件。`$info` 由我设置,
它对数据库对象进行特殊检查。

<?php
// var_format

function var_format($v) // pretty-print var_export
{
return (
str_replace(array("\n"," ","array"),
array(
"<br>","&nbsp;","&nbsp;<i>array</i>"),
var_export($v,true))."<br>";
}
function
myDie($info)
{
$mysqlerr=strpos($info,"ERROR=You have an error in your SQL syntax");
if(
$mysqlerr>0)$info=substr($info,0,$mysqlerr)." mySql格式错误";
$out="<br>MSG='$info'<br>".var_format($_REQUEST)."<br>";
$bt=debug_backtrace();
$sp=0;
$trace="";
foreach(
$bt as $k=>$v)
{
extract($v);
$file=substr($file,1+strrpos($file,"/"));
if(
$file=="db.php")continue; // 数据库对象
$trace.=str_repeat("&nbsp;",++$sp); //spaces(++$sp);
$trace.="文件=$file,行=$line,函数=$function<br>";
}
$out.="<br>".backTrace();
if(
substr($info,0,4)=="XXX ") // 数据库不可访问时的特殊错误
{
$out=str_replace("<br>","\n",$out);
$out=str_replace("&nbsp;"," ",$out);
mail("[email protected]","用户 $REMOTE_ADDR 的数据库执行错误","$out");
exit(
"数据库访问错误。请稍后再试。");
}
mail("[email protected]",'错误监控','执行错误',$out);
exit(span class="string">"糟糕!程序中出现执行错误,已发送给网站管理员。
如果您没有很快收到他的邮件,请联系他。"
);
}
?>

这将产生如下输出

文件=badmode.php,行=5,函数=backTrace
文件=login.php,行=209,函数=require
文件=midScreen.php,行=264,函数=require
文件=masterindex.php,行=161,函数=require
文件=production2.php,行=121,函数=require
文件=index.php,行=16,函数=require
frank at frank dot com
16年前
这是我的简单示例
代码打印实例化打印类的类的变量。

我相信你看代码的时候就能明白
打印结果:jippii

<?php
class A {

function
something() {
$s = debug_backtrace();

$callingObject = $s[1]['object'];
$test = $callingObject->jip;
print
$test;
}

}

class
B {
var
$jip;

function
execute() {
$a = new A();
$this->jip = "jippii";
$a->something();
}

}
$control = new B();
$control->execute();
?>
samthor
16年前
这是一种获取调用栈中上游函数参数的方法(适用于类方法、静态方法和非类方法)
<?php
/**
* getArgs - 获取上游方法的参数
* 可调用方式例如:"funcname","class::staticmethod","class->instancemethod"
*/
function getArgs( $target, $subclass_ok = true ) {

if(
strpos( $target, "::" ) ) {
list(
$class, $target ) = explode( "::", $target, 2 );
$type = "::";
}
else if(
strpos( $target, "->" ) ) {
list(
$class, $target ) = explode( "->", $target, 2 );
$type = "->";
}
else {
$type = NULL;
$class = NULL;
}
$class and $class = new ReflectionClass( $class );

foreach(
debug_backtrace() as $obj ) {

if(
$obj['function'] == $target ) {
if(
$type and $obj['type'] == $type ) {
$_cl = new ReflectionClass( $obj['class'] );
if(
$_cl->getName() == $class->getName() or ( $subclass_ok and $_cl->isSubclassOf( $class ) ) ) {
return
$obj['args'];
}
unset(
$_cl );
}
else if( !
$type ) {
return
$obj['args'];
}
}

}

return
NULL;

}
?>

一些使用示例
<?php
class Foo {
function
test() {
$args = getArgs( "Foo->base" );
print(
"我调用base方法时参数'v'为: "{$args[0]}"\n" );
}
function
base( $v ) {
$this->test();
}
}

$f = new Foo();
$f->base( 713 ); // 将打印 "..我调用base方法时参数'v'为: 713"

?>

相信我,这样做有一些原因 :)
bernyregeling AT hotmail DOT com
21年前
除了jlim,我还编写了此函数,用于生成简洁的非HTML输出。

结果与Java错误信息类似。希望您喜欢。

(顺便说一句,如果显示debug_backtrace,此函数也会终止脚本)
------------------------------
函数 debug_bt()
{
如果(!function_exists('debug_backtrace'))
{
echo 'function debug_backtrace 不存在'."\r\n";
return;
}
//echo '<pre>';
echo "\r\n".'----------------'."\r\n";
echo '调试回溯:'."\r\n";
echo '----------------'."\r\n";
foreach(debug_backtrace() as $t)
{
echo "\t" . '@ ';
if(isset($t['file'])) echo basename($t['file']) . ':' . $t['line'];
否则
{
// 如果未设置文件,则假定函数调用
// 来自PHP编译的源代码(例如XML回调)。
echo '<PHP内部代码>';
}

echo ' -- ';

if(isset($t['class'])) echo $t['class'] . $t['type'];

echo $t['function'];

if(isset($t['args']) && sizeof($t['args']) > 0) echo '(...)';
否则 echo '()';

echo "\r\n";
}
//echo '</pre>';
exit;
}
icefragment at gmail dot com
18年前
一个简单的类似Python的回溯。请注意,如果数组作为参数传递给函数,我不会递归进入数组。

函数 backtrace()
{
$bt = debug_backtrace();

echo("<br /><br />回溯 (最近的调用最后):<br /><br />\n");
for($i = 0; $i <= count($bt) - 1; $i++)
{
if(!isset($bt[$i]["file"]))
echo("[PHP核心调用函数]<br />");
否则
echo("文件: ".$bt[$i]["file"]."<br />");

if(isset($bt[$i]["line"]))
echo("&nbsp;&nbsp;&nbsp;&nbsp;行 ".$bt[$i]["line"]."<br />");
echo("&nbsp;&nbsp;&nbsp;&nbsp;调用的函数: ".$bt[$i]["function"]);

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

if($j != count($bt[$i]["args"]) - 1)
echo(", ");
}
}
echo("<br /><br />");
}
}
zmorris at mac dot com
18年前
嗨,我厌倦了使用我制作的 trace( $message, __FILE__, __LINE__ ) 函数。它强迫我包含文件和行参数(因为php没有宏),所以我决定制作一个替代方案。

只需使用 trace( '我的消息' ); 调用此新版本,它就会以比 debug_backtrace() 数组中存储的更清晰的方式打印出堆栈跟踪。它处理来自函数外部的跟踪、嵌套函数中的跟踪和包含文件中的跟踪,并且还以可以粘贴回php代码中以进行更快测试的方式显示函数!

注意 - 确保使用正确的行尾保存您的文件,以便行号能够正常工作,对于Mac OS X来说是unix。您可以在BBEdit每个窗口顶部的工具栏弹出菜单中找到此选项。

<?php

函数 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 ) ); // 其他情况,让PHP尝试打印
}

函数
trace( $msg )
{
echo
"<pre>\n";

//var_export( debug_backtrace() ); echo "</pre>\n"; return; // 此行显示底层运行情况

$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( '函数外部错误' );

函数
test( $param1, $param2, $param3, $param4 )
{
trace( 'test()函数内部错误' );
}

test( 1.1, "param2\n", array( 1 => "a\n", "b\n" => 2 ), false );

?>
jlim#natsoft.com.my
21年前
漂亮打印回溯。函数根据调用值缩进,为方便起见,文件使用 file:// 链接。

享受,John Lim

函数 adodb_backtrace($print=true)
{
$s = '';
如果 (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">';
如果 (isset($arr['class'])) $s .= $arr['class'].'.';
foreach($arr['args'] as $v) {
如果 (is_null($v)) $args[] = 'null';
否则如果 (is_array($v)) $args[] = 'Array['.sizeof($v).']';
否则如果 (is_object($v)) $args[] = 'Object:'.get_class($v);
否则如果 (is_bool($v)) $args[] = $v ? 'true' : 'false';
否则 {
$v = (string) @$v;
$str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
如果 (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>';
如果 ($print) print $s;
}
return $s;
}
spagmoid at yahoo dot NOSPAMcom
20年前
致:jlim#natsoft.com.my

很棒的函数,但是有一些bug。

在这一行
foreach($arr['args'] as $v)

修改为
$args = array();
如果(!empty($arr['args'])) foreach($arr['args'] as $v)

并且由于如果从错误处理程序调用,数组中不存在行和文件,

$Line = (isset($arr['line'])? $arr['line'] : "unknown");
$File = (isset($arr['file'])? $arr['file'] : "unknown");

并相应替换。

这是我的版本,格式不同
----------------------------------------

函数 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">';
如果 (isset($arr['class'])) $s .= $arr['class'].'.';
$args = array();
如果(!empty($arr['args'])) foreach($arr['args'] as $v)
{
如果 (is_null($v)) $args[] = 'null';
否则如果 (is_array($v)) $args[] = 'Array['.sizeof($v).']';
否则如果 (is_object($v)) $args[] = 'Object:'.get_class($v);
否则如果 (is_bool($v)) $args[] = $v ? 'true' : 'false';
否则
{
$v = (string) @$v;
$str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
如果 (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;
}
henzeberkheij at gmail dot com
14年前
我发现知道是否正在调用一个函数很有用。例如,在 Java 中,你通常会在函数开头打印一行包含函数名和参数的行。我想在 php 中实现相同的功能,因此我编写了以下类

<?php
Debug
{
private static
$calls;

public static function
log($message = null)
{
if(!
is_array(self::$calls))
self::$calls = array();

$call = debug_backtrace(false);
$call = (isset($call[1]))?$call[1]:$call[0];

$call['message'] = $message;
array_push(self::$calls, $call);
}
}
?>

在任何其他内容之前包含此类
用法:在函数开头使用 Debug::log($message);。

自己编写一个不错的打印数据的方法;
ciprian dot stingu at gmail dot com
13年前
我用来调试的函数
我缩短了变量名,并为了适应帖子而删除了第二个函数中的空格 :(

<?php
define
("LFP", './lt.log');
function
LogTrace($Argument, $lfn = LFP, $itw = ' ')
{
error_log("=====\r", 3, $lfn);
error_log("[BEGIN BACKTRACE]\r", 3, $lfn);
$it = '';
$Ts = array_reverse(debug_backtrace());
foreach(
$Ts as $T)
{
if(
$T['function'] != 'include' && $T['function'] != 'require' && $T['function'] != 'include_once' && $T['function'] != 'require_once')
{
$ft = $it . '<'. basename($T['file']) . '> on line ' . $T['line'];
if(
$T['function'] != 'LogTrace')
{
if(isset(
$T['class']))
$ft .= ' in method ' . $T['class'] . $T['type'];
else
$ft .= ' in function ';
$ft .= $Trace['function'] . '(';
}
else
$ft .= '(';
if(isset(
$T['args'][0]))
{
if(
$T['function'] != 'LogTrace')
{
$ct = '';
foreach(
$T['args'] as $A)
{
$ft .= $ct . LogVar($A, '', $it, $itw, 0);
$ct = $it . $itw . ',';
}
}
else
$ft .= LogVar($T['args'][0], '', $it, $itw, 0);
}
$ft .= $it . ")\r";
error_log($ft, 3, $lfn);
$it .= $itw;
}
}
error_log("[END BACKTRACE]\r", 3, $lfn);
}

function
LogVar(&$Var, $vn, $pit, $itw, $nlvl, $m = '')
{
if(
$nlvl>=16) return;
if(
$nlvl==0){$tv=serialize($Var);$tv=unserialize($tv);}
else
$tv=&$Var;
$it=$pit.$itw;
for(
$i=0; $i<$nlvl;$i++) $it.='.'.$itw;
$o='';$nl="\n";
if(
is_array($tv))
{
if(
strlen($vn)>0) $o.=$it.$m.'<array> $'.$vn.' = (';
else
$o.="\r".$it.$m.'<array> = (';
$o.= $nl;$AK=array_keys($tv);
foreach(
$AK as $AN) {$AV=&$tv[$AN];$o.=LogVar($AV,$AN,$pit,$itw,$nlvl+1);}
$o.=$it.')'.$nl;
}
else if(
is_string($tv))
{
if(
strlen($vn)>0)$o.=$it.$m.'<string> $'.$vn.' = ';
else
$o.=' '.$m.'<string> = ';
if(
$tv===null) $o.='NULL';
else
$o.='"'.$tv.'"';
$o.=$nl;
}
else if(
is_bool($tv))
{
if(
strlen($vn) > 0) $o.=$it.$m.'<boolean> $'.$vn.' = ';
else
$o.=' '.$m.'<boolean> = ';
if(
$tv===true) $o.='TRUE';
else
$o.='FALSE';
$o.=$nl;
}
else if(
is_object($tv))
{
if(
strlen($vn)>0)
{
$o.=$pit.$itw;
for(
$i=0;$i<$nlvl;$i++) $o.='.'.$itw;
$o.=$m.'<'.get_class($tv).'::$'.$vn.'> = {'.$nl;
}
else
$o.=' '.$m.'<'.get_class($tv).'::> = {'.$nl;
$R=new ReflectionClass($tv);
$o.=$it.'.'.$itw.'Class methods {'.$nl;
$CM=$R->getMethods();
foreach(
$CM as $MN => $MV)
{
$o.=$it.'.'.$itw.'.'.$itw.implode(' ',Reflection::getModifierNames($MV->getModifiers())).' '.$MV->getName().'(';
$MP=$MV->getParameters(); $ct='';
foreach(
$MP as $MPN => $MPV)
{
$o.=$ct; $o.=$MPV->isOptional()?'[':'';
if(
$MPV->isArray()) $o.='<array> ';
else if(
$MPV->getClass()!==null) $o.='<'.$MPV->getClass()->getName().'::> ';
$o.=$MPV->isPassedByReference()?'&':''; $o.='$'.$MPV->getName();
if(
$MPV->isDefaultValueAvailable())
{
if(
$MPV->getDefaultValue()===null) $o.=' = NULL';
else if(
$MPV->getDefaultValue()===true) $o.=' = TRUE';
else if(
$MPV->getDefaultValue()===false) $o.=' = FALSE';
else
$o.=' = '.$MPV->getDefaultValue();
}
$o.=$MPV->isOptional()?']':''; $ct=', ';
}
$o.=')'.$nl;
}
$o.=$it.'.'.$itw.'}'.$nl; $o.=$it.'.'.$itw.'Class properties {'.$nl;
$CV=$R->getProperties();
foreach(
$CV as $CN => $CV)
{
$M=implode(' ',Reflection::getModifierNames($CV->getModifiers())).' ';
$CV->setAccessible(true);
$o.=LogVar($CV->getValue($tv),$CV->getName(),$pit,$itw,$nlvl+2,$M);
}
$o.=$it.'.'.$itw.'}'.$nl; $o.=$it.'.'.$itw.'Object variables {'.$nl;
$OVs=get_object_vars($tv);
foreach(
$OVs as $ON => $OV) $o.=LogVar($OV,$ON,$pit,$itw,$nlvl+2);
$o.=$it.'.'.$itw.'}'.$nl; $o.=$pit.$itw;
for(
$i=0;$i<$nlvl;$i++) $o.='.'.$itw;
$o.='}'.$nl;
}
else
{
if(
strlen($vn)>0) $o.=$it.$m.'<'.gettype($tv).'> $'.$vn.' = '.$tv;
else
$o.=' '.$m.'<'.gettype($tv).'> = '.$tv;
$o.=$nl;
}
return
$o;
}
//test
date_default_timezone_set('Europe/Bucharest');
$date = new DateTime('2010-01-28');
LogTrace($date);
?>
aryel at iku dot com dot br
16年前
一个易于获取调试回溯所有细节的函数

<?php
function getDebugBacktrace($NL = "<BR>") {
$dbgTrace = debug_backtrace();
$dbgMsg .= $NL."调试回溯开始:$NL";
foreach(
$dbgTrace as $dbgIndex => $dbgInfo) {
$dbgMsg .= "\t 在 $dbgIndex ".$dbgInfo['file']." (第 {$dbgInfo['line']} 行) -> {$dbgInfo['function']}(".join(",",$dbgInfo['args'])")$NL";
}
$dbgMsg .= "调试回溯结束".$NL;
return
$dbgMsg;
}
?>

然后你可以在任何需要的地方调用它来获取一个包含可读格式调试回溯信息的字符串(例如,你的错误处理函数)。

<?php
$backtrace
= getDebugBacktrace();
echo
"致命错误!无法连接到数据库!";
echo
$backtrace;
?>

如果在命令行运行,你可能需要替换行分隔符。你可以通过函数参数来实现。

<?php
$backtrace
= getDebugBacktrace("\n");
echo
"错误!服务器资源耗尽!正在转储错误回溯";
echo
$backtrace;
?>

希望这有帮助!
Aryel
To Top