使用错误异常处理程序和关闭序列防止没有堆栈帧的异常


Preventing Exceptions without Stack Frames with Error Exception Handler and Shutdown Sequences

这一周我遇到了一个小问题。前面的错误信息是关于:

[30-Dec-2012 15:19:32] PHP致命错误:在第0行未知的情况下抛出异常

我认为这是因为我的错误处理程序(详见下文)将任何错误转换为异常。在没有堆栈框架的情况下,我可能应该防止这种情况。

是否有一个简单的方法来找出是否有任何堆栈框架在PHP?

细节:

在我的一个网站上,我有一个错误处理程序正在运行,它将每个错误转换为异常,准确地说是常见的ErrorException

我在一段时间前引入了它,因为该站点主要是遗留代码,我希望任何问题都能导致异常,我最终可以以一种流线型的方式"捕获"异常处理程序并停止请求。

我把它放到它自己的类中,处理程序被注册,并且并行地打开一个输出缓冲区来捕获输出,直到抛出异常。基本代码是这样的:

// register output buffering
$r = ob_start(array($this, 'handleBuffer'));
// register error handler       
$this->_originalErrorHandler = set_error_handler(array($this, 'handleError'));
// register exception handler
$this->_originalExceptionHandler = set_exception_handler(array($this, 'handleException'));

这一切都很好,直到我决定添加另一个输出缓冲类。只需要一个捕获所有输出,然后可以在网站上做一些"后期制作",包括检查HTML问题(是的,这都是一些遗留问题,所以实际上这有点像ducduc胶带,我知道)。顺便说一下,这也很有效。然而,当我在新组件中犯了一个错误时没有:

[30-Dec-2012 15:19:32] PHP致命错误:在第0行未知的情况下抛出异常

这基本上是我的问题。是否有一种简单的方法来防止出现这些错误?我多少知道为什么会出现错误,但我不是很确定,所以我很难真正规避这个问题。我试图在脚本进入新的关闭阶段之前释放新的输出缓冲区,因为我认为这会导致这种情况。

您的问题表明您正在使用EOL (生命终止)版本的PHP(特别是PHP <5.3.0),这意味着它不再被支持。这个问题是由于在没有帧存在的情况下抛出异常,因此旧引擎不知道如何正确处理这些异常。

这可能是由于几个不同的原因。一些最常见的例子如下:

  1. 你从错误处理程序或异常处理程序内部抛出一个异常。
  2. 你从析构函数内部抛出了一个异常。
  3. 你在回调函数内部抛出了一个异常(像一个输出缓冲回调函数)。

下面的例子演示了在这些情况下你的问题…

function myErrorHandler($errno, $errstr, $errfile, $errline)
{
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
function myExceptionHandler($exception) {
    echo "We got an exception with message: '{$exception->getMessage()}'";
}
function myCallBack($contents) {
    trigger_error('ohnoes!'); // You can't throw an error from the output buffer callback function in older versions of PHP < 5.3
}
class Foo {
    public function __destruct() {
        trigger_error('ohnoes!'); // You can't throw an error from a destructor in older versions of PHP < 5.3
    }
}
set_error_handler('myErrorHandler');
set_exception_handler('myExceptionHandler');

上面的代码将导致您看到这里描述的致命错误…

ob_start("myCallBack");

…这里……

$foo = new foo;

这个问题已经在PHP>= 5.3.0中修复了,所以如果你使用的是最新版本的PHP,你应该不会看到这个问题。

最简单的解决方法是升级PHP。如果这不是一个选项,你必须考虑这些事实:你不能在PHP不希望抛出异常的地方抛出异常(在回调函数、错误处理程序、异常处理程序等中)。——这些实际上都被认为是PHP的回调)。

另一件事是,您不应该以这种方式将每个错误都转换为异常。如果你正在做的是我提供的代码演示(即抛出一个异常从错误处理程序内部——从而把每个错误变成一个异常),那么你将给自己带来很多的痛苦和几乎没有任何好处。PHP错误不应该被处理。它们的目的是通知客户端有问题(客户端是编写PHP代码的人)或潜在的问题。处理错误本身并不像将每个错误转换为异常然后处理该异常那样简单,因为并非每个错误都应该是异常的。例如,E_NOTICE级别的错误在异常处理中没有位置。它们主要用于通知您潜在的错误,而不是您的代码中一定存在错误,更不用说大多数错误甚至无法在用户空间代码中轻松处理(它们大多需要重构代码本身)。我强烈建议不要这样做