Знает ли исключение о том, кто его возбудил и как?

FractalizeR

Новичок
Знает ли исключение о том, кто его возбудил и как?

Добрый день.

Меня интересует такой вопрос. Разрабатывая для себя небольшой простой класс для удобной работы с MySQL, я подумал, что было бы неплохо разработать и custom-класс исключения, который бы еще и выполнял очистку, если это требуется (скажем, Rollback транзакции, если она была начата).

Но проблема в том, что конструктор и деструктор класса исключения вызываются при любом варианте его использования , даже если имеет место блок catch. Но ведь если исключение перехвачено, это значит, код, его перехвативший знает, что делает и, возможно, ему вовсе не требуется выполнять эту самую очистку.

Можно ли из класса исключения узнать, было ли исключение перехвачено блоком catch или нет?

set_exception_handler не слишком красивое решение такой проблемы, так как этот обработчик должен знать все о всех классах исключений в проекте.
Исследование результата debug_backtrace тоже ничего не дает, так как блоки try-catch там не фиксируются.

Есть вариант вызова в catch блоках функции $MyException->noCleanupNeeded(), а очистку выполнять в деструкторе исключения. Но, может, есть более оригинальные решения?

[Сразу скажу, что, конечно, написание всяких таких классов уже по сто раз написанных другими разработчиками - изобретение велосипеда, тем не менее, для меня полезно в образовательных целях :) ]
 

bkonst

.. хочется странного?...
Класс исключения, вообще-то, должен содержать информацию об ошибке, а не править её.
 

fixxxer

К.О.
Партнер клуба
PHP:
class SqlException extends Exception{};
class RollbackSqlException extends SqlException{};
//...
try {
   $db->begin();
   if (smth_bad()) throw new RollbackSqlException;
   $db->commit();  
} catch (RollbackSqlException $e) {
   $db->rollback();
} catch (SqlException $e) {
   // ...
} catch (Exception $e) {
   // ...
}
как та так
 

FractalizeR

Новичок
Автор оригинала: bkonst
Класс исключения, вообще-то, должен содержать информацию об ошибке, а не править её.
Cleanup и исправление ошибки - это не совсем одно и то же, но в целом я с вами согласен. Тем не менее, теоретически вопрос остается.

Автор оригинала: bkonst
PHP:
if (smth_bad()) throw new RollbackSqlException;
Я имел ввиду не совсем это. Исключения может порождать сам класс работы с базой данных, а не внешний код, как в вашем случае. Кроме того, в вашем примере имеет место отдельный класс исключения для очистки, что означает, что вызывающий код должен знать, какой класс исключения возбудить. А если он это знает, то и rollback сам может вызвать.
 

bkonst

.. хочется странного?...
Автор оригинала: FractalizeR
Cleanup и исправление ошибки - это не совсем одно и то же, но в целом я с вами согласен. Тем не менее, теоретически вопрос остается.
По моему, если
а) исключение доползло до "верха" приложения и не было обработано - rolllback стоит сделать в любом случае, потому что неперехваченное исключение - признак того, что с приложением что-то сильно неладно;
б) исключение перехватил обработчик, он сам сможет решить, стоит ли делать rollback;
В обоих случаях сам объект исключения "принимать решения" не должен.

-~{}~ 22.12.07 21:48:

(Если же у нас есть cleanup-код, которой мог бы (в отличие от rollback) войти в finally-блок, выполняем его в локальном обработчике и снова выбрасываем исключение)
 

Pigmeich

Новичок
FractalizeR
Ну два варианта на выбор:
1. SqlExtension($db) - при возбуждении в конструкторе откатывать. Если ссылка на db нулевая - не откатывать

2. class BackTrace. При каждом действии которое надо откатить, добавляем в него функцию отката.
Затем, либо в catch заставляем выполнить все в обратном порядке, либо выполняем еще в конструкторе исключения.

Дополнительные разьяснения нужны?
 

FractalizeR

Новичок
Автор оригинала: bkonst
По моему, если
а) исключение доползло до "верха" приложения и не было обработано - rolllback стоит сделать в любом случае, потому что неперехваченное исключение - признак того, что с приложением что-то сильно неладно;
Согласен, но объект исключения не знает, обработано ли исключение или нет. И, судя по всему, узнать никак не может.

Автор оригинала: bkonst
б) исключение перехватил обработчик, он сам сможет решить, стоит ли делать rollback;
В обоих случаях сам объект исключения "принимать решения" не должен.
Да, наверное, вы правы. но в этом случае при использовании persistent соединений с базой данных для отката транзакции придется пользоваться set_exception_handler, либо все операции с базой в коде заключать в блоки try - catch с откатом транзакции и повторным возбуждения исключения.

Автор оригинала: bkonst
Ну два варианта на выбор:
1. SqlExtension($db) - при возбуждении в конструкторе откатывать. Если ссылка на db нулевая - не откатывать
В настоящий момент я остановился именно на этом варианте. Остается спорным вопрос, нужно ли всегда откатывать транзакцию, если вызвано исключение, но в моем случае это пока всегда так.

Автор оригинала: bkonst
2. class BackTrace. При каждом действии которое надо откатить, добавляем в него функцию отката.
Затем, либо в catch заставляем выполнить все в обратном порядке, либо выполняем еще в конструкторе исключения.
Тоже вариант, спасибо. Подумаю. Только скорее всего реализация такого класса - аналог register_shutdown_function.

Еще наверное можно сделать так: в модуле, в котором объявлена глобальная переменная $DBConnection вписать функцию очистки и зарегистрировать ее по register_shutdown_function. В этой функции проверять состояние транзакции и откатывать ее. В этом случае не нужно ни за чем больше следить. При завершении работы скрипта, было ли возбуждено исключение или нет, будет откат транзакции, если она начиналась. Конечно, это выносит функцию очистки за пределы классов работы с БД....


Я так понимаю, что ответ на вопрос "Знает ли исключение о том, кто его возбудил и как?", можно ответить - "Нет, не знает", правильно?
 

Pigmeich

Новичок
FractalizeR
Мой ник не bkonst

Я так понимаю, что ответ на вопрос "Знает ли исключение о том, кто его возбудил и как?", можно ответить - "Нет, не знает", правильно?
Знает с точностью до номера строки кода и описания ошибки.
 

bkonst

.. хочется странного?...
Автор оригинала: FractalizeR
Да, наверное, вы правы. но в этом случае при использовании persistent соединений с базой данных для отката транзакции придется пользоваться set_exception_handler, либо все операции с базой в коде заключать в блоки try - catch с откатом транзакции и повторным возбуждения исключения.
В целом - да, но есть делали:
1. set_exception_handler в этом случае совершенно не обязан знать о типа исключений (только о том, что необходимо "всё откатить" вне зависимости от конкретной проблемы)
2. try - catch с откатом транзакции не нужен из-за (1), а с более специфическим cleanup-кодом будет нужен в любом случае.
 
Сверху