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)它返回所有堆栈帧。

返回值

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

来自 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 个说明

114
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()
3
liam at N0SPAM dot boxclever dot ca
1 年前
通过位掩码参数提供的选项可以使用 ! 禁用

<?php
debug_backtrace
( !DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
?>
13
robert at medianis dot net
7 年前
关于 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
函数 F1()
{
echo
"<br />";
echo
"现在在 F1 中";
echo
"<pre>".print_r(debug_backtrace(2),true)."</pre>";
}

DebugOptionsTest
{
函数
F2()
{
echo
"<br />";
echo
"现在在 F2 中";
F1();
}

}

echo
"<hr />调用 F1";
F1();

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

?>
10
jsnell at e-normous dot com
17 年前
如果您在错误处理程序中使用 backtrace 函数,请避免对参数使用 var_export(),因为您会在某些情况下导致致命错误,从而阻止您看到堆栈跟踪。 某些结构将导致 PHP 生成致命错误“嵌套级别过深 - 递归依赖项?” 这是 php 的设计特性,而不是错误(请参阅 http://bugs.php.net/bug.php?id=30471
2
michael dot schramm at gmail dot com
14 年前
如果您将对象用作函数调用的参数,请小心!

<?php
error_reporting
(E_ALL);

函数
myPrint($trace){
遍历(
$trace $i=>$call){
/**
* 这很必要! 如果您所有的对象都有 __toString 函数,则不需要!
*
* 捕获致命错误:无法将类 B 的对象转换为字符串
* 捕获致命错误:无法将类 A 的对象转换为字符串
* 捕获致命错误:无法将类 B 的对象转换为字符串
*/
如果 (is_object($call['object'])) { $call['object'] = '转换后的类对象 '.get_class($call['object']); }
如果 (
is_array($call['args'])) {
遍历 (
$call['args'] 为 &$arg) {
如果 (
is_object($arg)) { $arg = '转换后的类对象 '.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);
}

A{
公共函数
test($obj){
$obj->test();
}
}

B{
公共函数
test(){
echo
myPrint(debug_backtrace());
}
}

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

$A->test($B);

?>
2
http://synergy8.com
18 年前
需要注意的是,如果 backtrace 中存在诸如 call_user_func 之类的内部 php 函数,则不会设置 'file' 和 'line' 条目。

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

<?php

$arrTrace
= debug_backtrace();

遍历 (
$arrTrace $arr)
{
如果 (!isset (
$arr['file']))
{
$arr['file'] = '[PHP 内核]';
}

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

// 做点什么
}

?>
6
Emmett Brosnan
7 年前
快速且简单的格式化输出,来自 debug_backtrace。

$file_paths = debug_backtrace();

遍历($file_paths 为 $file_path) {
遍历($file_path 为 $key => $var) {
如果($key == 'args') {
遍历($var 为 $key_arg => $var_arg) {
echo $key_arg . ': ' . $var_arg . '<br>';
}
} 否则 {
echo $key . ': ' . $var . '<br>';
}
}
}
4
jcmargentina at gmail dot com
4 年前
我想指出,新版 php 中的 debug_backtrace() 可以检测递归 // 循环引用 .. 避免内存消耗。

示例

<?php

ParentClass {
公共函数
__construct()
{
$this->_child = new ChildClass($this);
var_dump(debug_backtrace());
}
}

ChildClass {
公共函数
__construct(ParentClass $p)
{
$this->_parent = $p;
}
}

$test = new ParentClass();
?>

输出

数组(1) {
[0]=>
数组(7) {
["file"]=>
字符串(23) "/home/jcm/testdebug.php"
["line"]=>
整数(18)
["function"]=>
字符串(11) "__construct"
["class"]=>
字符串(11) "ParentClass"
["object"]=>
对象(ParentClass)#1 (1) {
["_child"]=>
对象(ChildClass)#2 (1) {
["_parent"]=>
*递归*
}
}
["type"]=>
字符串(2) "->"
["args"]=>
数组(0) {
}
}
}

注意提供的 *递归* 提示
3
root at jackyyf dot com
11 年前
使用 register_shutdown_function 时,该函数在关闭时被调用,没有关于此函数的行号或文件名信息,只提供函数、类(如果可能)、类型(如果可能)和参数。
3
Bill Getas
13 年前
这是我的一点更新贡献 - 它以我喜欢的格式打印彩色输出。 定义一个辅助函数 isRootIp(),其中包含一个包含您 IP 的数组;然后调用 bt() 只是返回,因此您可以在实时网站上随意添加 backtrace,而不会让任何人知道。

<?php
函数 bt()
{
如果( !
isRootIp() )
{
返回
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\'] ). "/";' ) );
}
?>
6
Anonymous
11 年前
简单的函数,用于获取形式为“文件名: [类->][函数(): ]”的字符串

<?php
function 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
function debug($str) {
echo
get_caller_info() . $str . "<br>\n";
}
?>

get_caller_info() 函数将返回调用 debug() 函数或类的信息。
3
jonas at faceways dot se
10 年前
使用 debug_backtrace() 检查是否被另一个调用者访问时,请记住要求 debug_backtrace() 只进行必要的深度检查,并跳过获取整个调试对象作为返回值。

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

例如
function foo($bar)
{
return 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";

在 PHP 5.3.10 中,我得到了
资源打开时: resource
资源关闭时: resource

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

请注意。
2
jlammertink at gmail dot com
14 年前
我使用这个简单但有效的函数,可以查看子类中的哪个方法调用了当前方法(在父类中)。

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

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

return
null;
}
?>
5
d at rren dot me
11 年前
大家好,只是一个提示 - 结果数组中的 ['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);
// 循环回溯
?>
1
kroczu AT interia DOT pl
17 年前
<?
// 有用且方便的调试函数
// 它显示内存使用情况和调用之间的时间流逝,因此我们可以快速找到需要优化的代码块...
// 示例结果
/*
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;
}

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

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();

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

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

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

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

请注意,此二进制文件为早期版本。
1
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"));

?>
8
kenorb at gmail dot com
13 年前
一行代码打印最简单、最短的可读堆栈跟踪:)
<?php
array_walk
(debug_backtrace(),create_function('$a,$b','print "{$a[\'function\']}()(".basename($a[\'file\']).":{$a[\'line\']}); ";'));
?>
1
jake at qzdesign dot co dot uk
5年前
`args` 元素只包含实际传递给函数或方法的参数。如果未明确指定默认参数,则不包含默认参数。(至少,PHP 7.1.9 的情况就是这样。)这与 `func_get_args()` 的行为一致。
2
匿名
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` 是被调用者。
1
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']));
}
}
}
?>
1
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;
}
?>
1
kexianbin at diyism dot com
11 年前
不需要服务器上的 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;
}
?>
2
php 新手
13 年前
令人惊讶的是,没有人描述过它最好的用途之一:转储一个变量并显示位置。在调试时,尤其是在调试一个大型且不熟悉的系统时,记住我在哪里添加了那些 var dump 很痛苦。此外,这种方法在多个 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
"Where called: line $line of $object \n(in $file)";
}
?>

此外,从任何函数调用“whereCalled()”可以快速识别执行意外操作的位置(例如,在错误的时间更新属性)。 我是 PHP 新手,但在 Perl 中使用过等效的函数很多年了。
2
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”属性未设置,因此该技术不可用。
0
henzeberkheij at gmail dot com
13 年前
我发现知道一个函数是否被调用很有用。 例如,在 Java 中,你通常会在函数开头打印一行带有函数名和参数的代码。 我想在 PHP 中实现同样的事情,所以我写了以下类

<?php
class 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);。

自己写一个关于数据的美观的打印结果。
0
john dot risken at gmail dot com
14 年前
每个人似乎都有自己喜欢的用法。 我用这个函数替换了 die()。 它会向用户显示一条消息
并给我发送一个关于错误发生情况的美观打印结果。 $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 format error";
$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; // the db object
$trace.=str_repeat("&nbsp;",++$sp); //spaces(++$sp);
$trace.="file=$file, line=$line, function=$function<br>";
}
$out.="<br>".backTrace();
if(
substr($info,0,4)=="XXX ") // special errrors when db is inaccessible
{
$out=str_replace("<br>","\n",$out);
$out=str_replace("&nbsp;"," ",$out);
mail("[email protected]","Database Execution Error for user ".$REMOTE_ADDR,"$out");
exit(
"Database Access Error. Please try again later.");
}
mail("[email protected]",'Error Monitor','Execution Error',$out);
exit(
"DANG! An execution error in the program has been sent to the webmaster.
If you don't get an email from him soon, please call him."
);
}
?>

这会产生类似于以下内容的输出

file=badmode.php, line=5, function=backTrace
file=login.php, line=209, function=require
file=midScreen.php, line=264, function=require
file=masterindex.php, line=161, function=require
file=production2.php, line=121, function=require
file=index.php, line=16, function=require
0
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();
?>
2
bernyregeling AT hotmail DOT com
21 年前
除了 jlim 之外,我还写了这个函数,用于提供漂亮的无 HTML 输出。

结果与 Java 错误相似。希望你喜欢。

(顺便说一下,如果显示 debug_backtrace,此函数也会退出脚本)
------------------------------
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
{
// 如果未设置文件,我假设函数调用
// 来自 PHP 编译的源代码(例如 XML 回调)。
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;
}
0
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(
"the parameter 'v' to my call of base was: {$args[0]}\n" );
}
function
base( $v ) {
$this->test();
}
}

$f = new Foo();
$f->base( 713 ); // 将打印.. ".. my call of base was: 713"

?>

相信我,你可能想要这样做有一些原因 :)
0
icefragment at gmail dot com
17 年前
一个简单的 Python 式回溯。请注意,如果数组作为函数参数传递,我不会递归到数组中。

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 />");
}
}
0
zmorris at mac dot com
17 年前
嗨,我厌倦了使用我自己制作的 trace( $message, __FILE__, __LINE__ ) 函数。它迫使我包含文件和行参数(因为 php 没有宏),所以我决定制作一个替代方案。

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

注意 - 确保使用正确的行结尾保存你的文件,以便行号正确工作,对于 Mac OS X 来说是 unix。你可以在 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 );

?>
1
jlim#natsoft.com.my
21 年前
以美观格式显示 backtrace()。函数根据调用值进行缩进,文件使用 file:// 链接,方便查看。

尽情享受,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;
}
-1
spagmoid at yahoo dot NOSPAMcom
20 年前
致:jlim#natsoft.com.my

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

在以下代码行
foreach($arr['args'] as $v)

将其更改为
$args = array();
if(!empty($arr['args'])) foreach($arr['args'] as $v)

由于从错误处理程序调用时,数组中没有 line 和 file,因此请使用

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

并进行相应替换。

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

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;
}
-3
ciprian dot stingu at gmail dot com
12 年前
我用于调试的函数
我缩短了变量名,并从第二个函数中去掉了空格,以适合发布 :(

<?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);
?>
-4
aryel at iku dot com dot br
16 年前
一个简单函数,用于提取调试 backtrace 的所有详细信息

<?php
function getDebugBacktrace($NL = "<BR>") {
$dbgTrace = debug_backtrace();
$dbgMsg .= $NL."Debug backtrace begin:$NL";
foreach(
$dbgTrace as $dbgIndex => $dbgInfo) {
$dbgMsg .= "\t at $dbgIndex ".$dbgInfo['file']." (line {$dbgInfo['line']}) -> {$dbgInfo['function']}(".join(",",$dbgInfo['args'])")$NL";
}
$dbgMsg .= "Debug backtrace end".$NL;
return
$dbgMsg;
}
?>

然后,您可以在任何需要获取可读格式调试 backtrace 字符串的地方调用它(例如,您的错误处理函数)

<?php
$backtrace
= getDebugBacktrace();
echo
"Fatal error! Cannot connect to database!";
echo
$backtrace;
?>

如果在命令行上运行,您可能需要替换行分隔符。可以通过函数参数执行此操作

<?php
$backtrace
= getDebugBacktrace("\n");
echo
"Error! Server is running out of foos! Dumping error backtrace";
echo
$backtrace;
?>

希望这有帮助,
Aryel
To Top