Захват нескольких типов исключений в одном блоке catch
Я хотел бы получить более чистый способ получить следующую функциональность, чтобы поймать AError
и BError
в одном блоке:
try
{
/* something */
}
catch( AError, BError $e )
{
handler1( $e )
}
catch( Exception $e )
{
handler2( $e )
}
Есть ли способ сделать это? Или мне нужно их поймать отдельно?
AError
и BError
имеют общий базовый класс, но они также передают его другим типам, которые я хотел бы пропустить до handler2
, поэтому я не могу просто ухватить базовый класс.
Ответы
Ответ 1
Если вы можете изменить исключения, использовать этот ответ.
Если вы не можете, вы можете попробовать поймать все с помощью Exception
, а затем проверить, какое исключение было выбрано с помощью instanceof
.
try
{
/* something */
}
catch( Exception $e )
{
if ($e instanceof AError OR $e instanceof BError) {
// It either an A or B exception.
} else {
// Keep throwing it.
throw $e;
}
}
Но, вероятно, было бы лучше использовать несколько блоков catch, как описано в вышеупомянутом ответе.
try
{
/* something */
}
catch( AError $e )
{
handler1( $e );
}
catch ( BError $b )
{
handler2( $e );
}
Ответ 2
Несмотря на то, что говорят эти другие ответы, вы можете поймать AError
и BError
в одном блоке (это несколько проще, если вы определяете исключения). Даже если у вас есть исключения, которые вы хотите "провалить", вы все равно сможете определить иерархию в соответствии с вашими потребностями.
abstract class MyExceptions extends \Exception {}
abstract class LetterError extends MyExceptions {}
class AError extends LetterError {}
class BError extends LetterError {}
Тогда:
catch(LetterError $e){
//voodoo
}
Как вы можете видеть здесь и здесь, даже Исключения SPL
по умолчанию имеют иерархию, которую вы можете использовать. Кроме того, как указано в PHP Manual:
Когда генерируется исключение, код, следующий за инструкцией, не будет выполненный, и PHP попытается найти первый соответствующий блок catch.
Это означает, что вы также можете иметь
class CError extends LetterError {}
который вам нужно обрабатывать иначе, чем AError
или BError
, поэтому ваш оператор catch будет выглядеть следующим образом:
catch(CError $e){
//voodoo
}
catch(LetterError $e){
//voodoo
}
Если бы у вас был случай, когда было двадцать или более исключений, которые законно принадлежали под одним и тем же суперклассом, и вам нужно было обрабатывать пять (или любую другую большую группу) одного из них, а остальное - другого, вы можете STILL сделайте это.
interface Group1 {}
class AError extends LetterError implements Group1 {}
class BError extends LetterError implements Group1 {}
И затем:
catch (Group1 $e) {}
Использование OOP, когда дело доходит до исключений, очень сильно. Использование таких вещей, как get_class
или instanceof
, - это взломы, и их следует избегать, если это возможно.
Еще одно решение, которое я хотел бы добавить, заключается в том, чтобы поместить функцию обработки исключений в свой собственный метод.
У вас может быть
function handleExceptionMethod1(Exception $e)
{
//voodoo
}
function handleExceptionMethod2(Exception $e)
{
//voodoo
}
Предполагая, что вы не можете контролировать иерархии или интерфейсы классов исключений (и почти всегда это будет путь), вы можете сделать следующее:
try
{
stuff()
}
catch(ExceptionA $e)
{
$this->handleExceptionMethod1($e);
}
catch(ExceptionB $e)
{
$this->handleExceptionMethod1($e);
}
catch(ExceptionC $e)
{
$this->handleExceptionMethod1($e);
}
catch(Exception $e)
{
$this->handleExceptionMethod2($e);
}
Таким образом, у вас все еще есть только одно местоположение кода, которое вы должны изменить, если ваш механизм обработки исключений должен измениться, и вы работаете в общих конструкциях ООП.
Ответ 3
Приходит PHP 7.1 - это способность ловить несколько типов.
Чтобы это:
<?php
try {
/* ... */
} catch (FirstException $ex) {
$this->manageException($ex);
} catch (SecondException $ex) {
$this->manageException($ex);
}
?>
и
<?php
try {
} catch (FirstException | SecondException $ex) {
$this->manageException($ex);
}
?>
являются функционально эквивалентными.
Ответ 4
Начиная с PHP 7.1,
catch( AError | BError $e )
{
handler1( $e )
}
интересно, вы также можете:
catch( AError | BError $e )
{
handler1( $e )
} catch (CError $e){
handler2($e);
} catch(Exception $e){
handler3($e);
}
и в более ранних версиях PHP:
catch(Exception $ex){
if($ex instanceof AError){
//handle a AError
} elseif($ex instanceof BError){
//handle a BError
} else {
throw $ex;//an unknown exception occured, throw it further
}
}
Ответ 5
В этой статье рассматривается вопрос electrictoolbox.com/php-catch-multiple-exception-types. Содержание сообщения, скопированное непосредственно из статьи:
Примеры исключений
Вот некоторые примеры исключений, которые были определены для целей этого примера:
class FooException extends Exception
{
public function __construct($message = null, $code = 0)
{
// do something
}
}
class BarException extends Exception
{
public function __construct($message = null, $code = 0)
{
// do something
}
}
class BazException extends Exception
{
public function __construct($message = null, $code = 0)
{
// do something
}
}
Обработка нескольких исключений
Это очень просто - для каждого типа исключений может быть блокировка catch:
try
{
// some code that might trigger a Foo/Bar/Baz/Exception
}
catch(FooException $e)
{
// we caught a foo exception
}
catch(BarException $e)
{
// we caught a bar exception
}
catch(BazException $e)
{
// we caught a baz exception
}
catch(Exception $e)
{
// we caught a normal exception
// or an exception that wasn't handled by any of the above
}
Если генерируется исключение, которое не обрабатывается никакими другими операторами catch, оно будет обрабатываться блоком catch (Exception $e). Это необязательно должно быть последним.
Ответ 6
В качестве расширения для принятого ответа вы можете переключить тип исключения, в результате получившийся образец, который похож на исходный пример:
try {
// Try something
} catch (Exception $e) {
switch (get_class($e)) {
case 'AError':
case 'BError':
// Handle A or B
break;
case 'CError':
// Handle C
break;
case default:
// Rethrow the Exception
throw $e;
}
}
Ответ 7
Здесь разумная альтернатива, если у вас нет контроля над определением исключений. Используйте имя переменной исключения для категоризации исключений, когда они пойманы. Затем проверьте переменную исключения после блока try/catch.
$ABError = null;
try {
// something
} catch (AError $ABError) { // let the exception fall through
} catch (BError $ABError) { // let the exception fall through
} catch (Exception $e) {
handler2($e);
}
if ($ABError) {
handler1($ABError);
}
Этот несколько необычный подход, вероятно, стоит того, только если существует много дублирования между реализациями блока catch.
Ответ 8
Помимо провала, можно также перешагнуть с помощью goto.
Это очень полезно, если вы хотите, чтобы мир горел.
<?php
class A_Error extends Exception {}
class B_Error extends Exception {}
class C_Error extends Exception {}
try {
throw new A_Error();
}
catch (A_Error $e) { goto abc; }
catch (B_Error $e) { goto abc; }
catch (C_Error $e) {
abc:
var_dump(get_class($e));
echo "Gotta Catch 'Em All\n";
}
3v4l.org
Ответ 9
Отличный способ - использовать set_exception_handler
.
Внимание!!! с PHP 7, вы можете получить белый экран смерти для фатальных ошибок. Например, если вы вызываете метод не-объекта, вы обычно получаете Fatal error: Call to a member function your_method() on null
, и вы ожидаете увидеть это, если включена отчет об ошибках.
Вышеупомянутая ошибка НЕ будет обнаружена с помощью catch(Exception $e)
.
Вышеприведенная ошибка НЕ запускает любой настраиваемый обработчик ошибок, установленный set_error_handler
.
Вы должны использовать catch(Error $e){ }
для обнаружения ошибок в PHP7.,
Это может помочь:
class ErrorHandler{
public static function excep_handler($e)
{
print_r($e);
}
}
set_exception_handler(array('ErrorHandler','excep_handler'));
Ответ 10
Другим вариантом, не указанным здесь, является использование атрибута code
исключения, поэтому вы можете сделать что-то вроде этого:
try {
if (1 === $foo) {
throw new Exception(sprintf('Invalid foo: %s', serialize($foo)), 1);
}
if (2 === $bar) {
throw new Exception(sprintf('Invalid bar: %s', serialize($foo)), 2);
}
} catch (Exception $e) {
switch ($e->getCode()) {
case 1:
// Special handling for case 1
break;
case 2:
// Special handling for case 2
break;
default:
// Special handling for all other cases
}
}