在异常处理程序中抛出异常

13

我有一个带有异常处理程序的脚本。在异常出现之前,这个异常处理程序会清理一些连接,并使脚本退出。

我想从这个异常处理程序中重新抛出异常,以便PHP自己的最后的异常处理程序处理它,在那里错误将被写入PHP的错误日志中,或者按照在PHP.ini中配置的默认方式进行处理。

不幸的是,这似乎是不可能的,如此处所述:

http://www.php.net/manual/en/function.set-exception-handler.php#68712

会导致致命错误:没有堆栈帧抛出异常

是否有另一种方法将错误冒泡到堆栈中,以便PHP在我的异常处理程序完成清理后处理它?


1
使用自定义异常处理程序仍会触发致命错误并记录错误。 - netcoder
为什么不直接从处理程序中再次抛出异常?如果你知道如何做,它是有效的。这将触发PHP最后的救援处理程序,这正是你要寻找的。 - hakre
我以前见过这个问题(并且建议恢复错误处理程序),但是现在我无法复现!这个问题是否与 PHP 的某个版本或 .ini 设置有关? - jlb
5个回答

18

异常处理程序中无法重新抛出异常,但是有其他地方可以这样做。例如,您可以将重新抛出与处理程序解耦,通过封装到自己的类中,然后使用 __destruct() 函数(PHP 5.3,示例):

<?php

class ExceptionHandler
{
    private $rethrow;
    public function __construct()
    {
        set_exception_handler(array($this, 'handler'));
    }
    public function handler($exception)
    {
        echo  "cleaning up.\n";
        $this->rethrow = $exception;
    }
    public function __destruct()
    {
        if ($this->rethrow) throw $this->rethrow;
    }
}

$handler = new ExceptionHandler;

throw new Exception();
将此添加到我的错误日志中:
[29-Oct-2011 xx:32:25] PHP Fatal error: Uncaught exception 'Exception' in /.../test-exception.php:23
Stack trace:
#0 {main}
thrown in /.../test-exception.php on line 23

1
另外还可以参考与__destruct有关的内容:如何确定PHP脚本处于终止阶段? - hakre
谢谢@hakre,你的解决方案可行。虽然它更像是一种变通方法,但我理解为什么需要这样做。 - Brad
嘿,布拉德,感谢接受。我喜欢这个“解决方法”比在php.net用户评论中创建自己的错误日志条目的建议更好。为什么要重复造轮子呢?我也尝试过注册关闭函数,但我更喜欢这个。 - hakre

8

只需捕获异常并自行记录消息,然后重新抛出即可。

try {
    $foo->doSomethingToCauseException();
} catch (Exception $e) {
    error_log($e->getMessage());
    throw $e;
}

如果您的 PHP 代码发生错误而无法处理,系统会抛出未捕获的异常。

在这种情况下,程序将不能正常运行。

一个未捕获的异常对我来说没问题。我想要将所有异常冒泡到顶层。我现在正在记录错误(使用您描述的确切方法),但出于一致性考虑,我宁愿让PHP处理它。 - Brad
我明白了。我也很好奇。根据我的经验,PHP处理异常的唯一方式就是让页面崩溃,哈哈。 - Mike Purcell

3

将导致致命错误: 异常抛出时没有堆栈帧

这个错误意味着你的异常是从脚本之外的代码中抛出的(就PHP而言)。这种代码的例子包括使用set_exception_handler()设置的自定义异常处理程序和任何类析构方法。在这样的代码中,唯一的选择是不要从中抛出异常。

如果你想使用PHP原生的错误处理,我建议你调用trigger_error()代替。如果你没有自定义的错误处理程序并且使用了适当的错误类型,它应该记录错误。例如,E_USER_ERROR应该可以。


或者再次抛出异常,根据配置将会给出适当的回溯信息。 - hakre
hakre:只需尝试一下(从自定义异常处理程序或类析构方法中抛出任何内容),您会发现它不起作用。无论您是否使用xdebug都没有关系。 - Mikko Rantalainen
1
Mikko,看起来我的答案需要PHP 5.3。同时请参考http://codepad.viper-7.com/jamrqP - hakre

2

只需将异常重新抛出为RunTimeException,它将保留堆栈跟踪 :)

try {
    // bad exception throwing code
} catch (Exception $e) {
    throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
}

0

抱歉,这个不起作用。restore_exception_handler()在异常中没有效果。 - Brad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接