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

Eri*_*zen 5 php destructor exception 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();
Run Code Online (Sandbox Code Playgroud)

这样做的目的是实现 D 的scope语句,该语句可作为多种其他语言的库使用。这使您可以摆脱嵌套的 try-catch 块,从而更轻松地正确执行回滚事务。

附录1:

我环顾过 Zend PHP 引擎,executor_globals.exception似乎就是我正在寻找的东西(https://github.com/php/php-src/blob/master/Zend/zend_globals.h)。然而,nullptr当我在 __destruct() 期间检查它时,该值始终是。知道我接下来应该看哪里吗?

附录2:

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

附录3:

我找到了以下代码(第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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这似乎积极阻止我做我想做的事,并解释了为什么executor_globals.exception总是如此nullptr

Mar*_*leu 1

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

实现自定义异常类

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

class Destroyer {
    public function __destruct() {
        if(MyException::exceptionThrown() {
            Database::rollback();
        } else {
            Database::commit();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)