老实说,Exception::getTraceAsString() 简直烂透了,只列出了调用的方法(例如,在下面的代码中,第 89 行调用了 fail2() 函数,但没有关于 fail1() 作为发起者的任何信息)。事实上,在下面的示例中,异常在第 78 行被抛出,而这完全没有包含在跟踪中,只有在异常中才能获得。链式异常也不支持。
示例
#0 /var/htdocs/websites/sbdevel/public/index.php(70): seabird\test\C->exc()
#1 /var/htdocs/websites/sbdevel/public/index.php(85): seabird\test\C->doexc()
#2 /var/htdocs/websites/sbdevel/public/index.php(89): seabird\test\fail2()
#3 /var/htdocs/websites/sbdevel/public/index.php(93): seabird\test\fail1()
#4 {main}
jTraceEx() 提供了更好的类似 Java 的堆栈跟踪,包括对链式异常的支持
异常:从类 C 抛出
at seabird.test.C.exc(index.php:78)
at seabird.test.C.doexc(index.php:70)
at seabird.test.fail2(index.php:85)
at seabird.test.fail1(index.php:89)
at (main)(index.php:93)
Caused by: 异常:从类 B 抛出
at seabird.test.B.exc(index.php:64)
at seabird.test.C.exc(index.php:75)
... 4 more
Caused by: 异常:从类 A 抛出
at seabird.test.A.exc(index.php:46)
at seabird.test.B.exc(index.php:61)
... 5 more
(见结尾处的示例代码)
<?php
function jTraceEx($e, $seen=null) {
$starter = $seen ? 'Caused by: ' : '';
$result = array();
if (!$seen) $seen = array();
$trace = $e->getTrace();
$prev = $e->getPrevious();
$result[] = sprintf('%s%s: %s', $starter, get_class($e), $e->getMessage());
$file = $e->getFile();
$line = $e->getLine();
while (true) {
$current = "$file:$line";
if (is_array($seen) && in_array($current, $seen)) {
$result[] = sprintf(' ... %d more', count($trace)+1);
break;
}
$result[] = sprintf(' at %s%s%s(%s%s%s)',
count($trace) && array_key_exists('class', $trace[0]) ? str_replace('\\', '.', $trace[0]['class']) : '',
count($trace) && array_key_exists('class', $trace[0]) && array_key_exists('function', $trace[0]) ? '.' : '',
count($trace) && array_key_exists('function', $trace[0]) ? str_replace('\\', '.', $trace[0]['function']) : '(main)',
$line === null ? $file : basename($file),
$line === null ? '' : ':',
$line === null ? '' : $line);
if (is_array($seen))
$seen[] = "$file:$line";
if (!count($trace))
break;
$file = array_key_exists('file', $trace[0]) ? $trace[0]['file'] : 'Unknown Source';
$line = array_key_exists('file', $trace[0]) && array_key_exists('line', $trace[0]) && $trace[0]['line'] ? $trace[0]['line'] : null;
array_shift($trace);
}
$result = join("\n", $result);
if ($prev)
$result .= "\n" . jTraceEx($prev, $seen);
return $result;
}
?>
以下示例代码:
<?php
class A {
public function exc() {
throw new \Exception('Thrown from class A'); }
}
class B {
public function exc() {
try {
$a = new A;
$a->exc(); }
catch(\Exception $e1) {
throw new \Exception('Thrown from class B', 0, $e1); }
}
}
class C {
public function doexc() {
$this->exc(); }
public function exc() {
try {
$b = new B;
$b->exc(); }
catch(\Exception $e1) {
throw new \Exception('Thrown from class C', 0, $e1); }
}
}
function fail2() {
$c = new C;
$c->doexc(); }
function fail1() {
fail2(); }
try {
fail1(); }
catch(\Exception $e) {
echo jTraceEx($e);
}
?>