Я хочу перехватить пользовательское исключение, выполнить некоторую логику с ним, и, если оно потерпит неудачу, выбросить его в более общий блок catch. Вот так:
class My_Exception extends Exception {
protected $_nonFatal = false;
public function __construct($value, $nonFatal = false) {
$this->_nonFatal = (bool)$nonFatal;
return parent::__construct($value);
}
public function isNonFatal() {
return (bool)$this->_nonFatal;
}
}
try {
} catch (My_Exception $e) {
if ($e->isNonFatal) {
// #1
// Err gently
}
// #2
// The error was fatal, so keep throwing it
throw new Exception($e->getMessage());
} catch (Exception $e) {
// #3
echo 'An exception was caught with the message '.$e->getMessage();
}
Так что я ловлю My_Exception
проверьте, не является ли это нефатальной ошибкой. Если это так, ошибочно, (раздел № 1). Если нет (раздел № 2), я хочу, чтобы был запущен второй улов (раздел № 3). Вместо этого throw new Exception
в разделе № 2 выходит try/catch/catch
все вместе.
Я мог бы использовать оператор switch в классе $e
но я думаю, что это грязнее Есть ли стандартный способ сделать это, что мне не хватает?
Если нет (раздел № 2), я хочу, чтобы был запущен второй улов
Блоки try / catch работают не так — блок catch всегда будет выходить из блока try / catch; он не «проваливается», как операторы switch.
Вам нужно обернуть свой код другим блоком try / catch.
try {
try {
// ...
} catch (My_Exception $e) {
if ($e->isNonFatal) {
// #1
// Err gently
}
// #2
// The error was fatal, so keep throwing it
throw new Exception($e->getMessage());
}
} catch (Exception $e) {
// #3
echo 'An exception was caught with the message '.$e->getMessage();
}
Есть ли стандартный способ сделать это, что мне не хватает?
Там нет реального «стандартного» способа сделать это либо; обычно вы не обобщаете исключение с другим исключением, но если приложение корректно, то именно так вы и захотите это сделать.
Лучший способ управлять цепочкой исключений — это передать ваше пользовательское исключение предыдущее исключение в конструкторе. Таким образом, есть вероятность, что любой, кто поднимет исключение, может использовать Exception.getPrevious()
и отобразите свое исключение вместе с более общим.
К сожалению, блоки catch не могут позаботиться о каком-либо исключении, которое выдается внутри предыдущего блока catch, даже если оно было вызвано другим вызовом функции.
Другими словами, любое необработанное исключение, выброшенное изнутри блока catch #1
не будет виден изнутри блока № 2 (но вместо этого будет обрабатываться четным верхним блоком catch в стеке вызовов):
try {
error_prone_code();
} catch (My_Exception $e) { // #1
throw new \Exception($e->getMessage());
} catch (Exception $e) { // #2
echo "Something bad happend: {$e->getMessage()}";
}
затем, честно говоря, сразу вложенные блоки try / catch выглядят довольно некрасиво.
Если логика вашего приложения не слишком сложна, вы можете сохранить свой код в чистоте и порядке, используя экземпляр оператор.
try {
error_prone_code();
// Make sure to catch the standard Exception base class
} catch (\Exception $e) {
// instanceof operator tells if object:
// is an instance of a specific class
// is an instance of some class that extends the mentionned class
// 's class implements a specific interface
if ( ! $e instanceof My_Exception || ! $e->isNonFatal() ) {
echo "Fatal exception arose: {$e->getMessage()}";
// You may also throw the same exception to some catch block upper in the call stack.
// throw $e;
} else {
// Gentle handling
}
}