在__destruct()中,如何查看当前是否有异常?

时间:2015-10-18 15:56:45

标签: php exception destructor php-internals

如何查看当前是否有异常,即堆栈正在展开?

在下面的示例中,您将如何实现isExceptionInFlight()

<?php

class Destroyer
{
    function __destruct()   {
        if (isExceptionInFlight()) {
            echo 'failure';
        } else {
            echo 'success';
        }
    }
}

function isExceptionInFlight() {
    // ?????
}

function createAndThrow()
{
    $var = new Destroyer;
    throw new \Exception;
}

createAndThrow();

这样做的目的是实现D scope语句,该语句可以作为多种其他语言的库使用。这允许您摆脱嵌套的try-catch块,这反过来使得更容易正确地进行回滚事务。

Addendum1:

我在Zend PHP引擎中环顾四周,executor_globals.exception似乎是我正在寻找的(https://github.com/php/php-src/blob/master/Zend/zend_globals.h)。但是,当我在__destruct()期间检查它时,此值始终为nullptr。我知道接下来要去哪看?

Addendum2:

检查executor_globals.opline_before_exception已取得一些进展。但是,当捕获到异常时,它不会重置为nullptr

Addendum3:

我找到了following code (line 135)

/* Make sure that destructors are protected from previously thrown exceptions.
 * For example, if an exception was thrown in a function and when the function's
 * local variable destruction results in a destructor being called.
 */
old_exception = NULL;
if (EG(exception)) {
    if (EG(exception) == object) {
        zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
    } else {
        old_exception = EG(exception);
        EG(exception) = NULL;
    }
}
zend_call_method_with_0_params(&obj, object->ce, &destructor, ZEND_DESTRUCTOR_FUNC_NAME, NULL);
if (old_exception) {
    if (EG(exception)) {
        zend_exception_set_previous(EG(exception), old_exception);
    } else {
        EG(exception) = old_exception;
    }
}

这似乎主动阻止我做我想做的事,并解释为什么executor_globals.exception总是nullptr

1 个答案:

答案 0 :(得分:1)

虽然我不推荐,但我过去已实施过。我的方法(简单地说)是这样的:

实现自定义异常类

class MyException extends Exception {
    public static $exceptionThrown = false;

    public function __construct($your parameters) {
         self::$exceptionThrown = true;
    }

}

现在,每个异常都应该是您自己的异常实现,而不是默认的Exception。

class Destroyer {
    public function __destruct() {
        if(MyException::exceptionThrown() {
            Database::rollback();
        } else {
            Database::commit();
        }
    }
}