Захват нескольких типов исключений в одном блоке 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
    }
}