Как я могу узнать, находится ли исключение в данный момент в полете, то есть стек раскручивается?
В приведенном ниже примере, как бы вы реализовали 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, что, в свою очередь, упрощает правильное выполнение транзакций с откатами.
Я посмотрел в Zend PHP Engine и executor_globals.exception
кажется то, что я ищу (https://github.com/php/php-src/blob/master/Zend/zend_globals.h). Однако это значение всегда nullptr
когда я проверяю это во время __destruct (). Есть идеи, где мне искать дальше?
инспектирование executor_globals.opline_before_exception
привело к некоторому прогрессу. Однако это не сбрасывается на nullptr
когда исключение было поймано.
Я нашел следующий код (строка 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
,
Хотя я не рекомендую, я реализовал это в прошлом. Мой подход был (проще говоря) так:
Реализуйте пользовательский класс Exception
class MyException extends Exception {
public static $exceptionThrown = false;
public function __construct($your parameters) {
self::$exceptionThrown = true;
}
}
Теперь каждое исключение должно быть вашей собственной реализацией исключения вместо исключения по умолчанию.
class Destroyer {
public function __destruct() {
if(MyException::exceptionThrown() {
Database::rollback();
} else {
Database::commit();
}
}
}
Других решений пока нет …