Перехват нескольких типов исключений в одном блоке перехвата - PullRequest
214 голосов
/ 09 декабря 2011

Я бы хотел более понятный способ получить следующую функциональность, чтобы поймать AError и BError в одном блоке:

try
{
    /* something */
}
catch( AError, BError $e )
{
    handler1( $e )
}
catch( Exception $e )
{
    handler2( $e )
}

Есть ли способ сделать это? Или я должен ловить их отдельно?

AError и Berror имеют общий базовый класс, но они также делят его с другими типами, к которым я бы хотел перейти на handler2, поэтому я не могу просто поймать базовый класс.

Ответы [ 11 ]

271 голосов
/ 11 октября 2013

Обновление:

Начиная с PHP 7.1, это доступно.

Синтаксис:

try
{
    // Some code...
}
catch(AError | BError $e)
{
    // Handle exceptions
}
catch(Exception $e)
{
    // Handle the general case
}

https://wiki.php.net/rfc/multiple-catch

https://github.com/php/php-src/commit/0aed2cc2a440e7be17552cc669d71fdd24d1204a


Для PHP до 7.1:

Несмотря на то, что говорят эти другие ответы, вы можете поймать 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
}

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

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

А затем:

catch (Group1 $e) {}

Использование ООП, когда речь идет об исключениях, очень эффективно.Использование таких вещей, как 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);
}

Таким образом, у вас все еще есть единственное местоположение кода, которое вы должны изменить, если ваш механизм обработки исключений должен измениться, и вы работаете в общих конструкциях ООП.

226 голосов
/ 09 декабря 2011

В PHP> = 7.1 это возможно. См. ответ ниже.


Если вы можете изменить исключения, используйте этот ответ .

Если вы не можете, вы можете попробовать перехватить все с помощью Exception, а затем проверить, какое исключение было сгенерировано с помощью instanceof.

try
{
    /* something */
}
catch( Exception $e )
{
    if ($e instanceof AError OR $e instanceof BError) {
       // It's either an A or B exception.
    } else {
        // Keep throwing it.
        throw $e;
    }
}

Но было бы, вероятно, лучше использовать несколько блоков улова, как описано в вышеупомянутом ответе .

try
{
    /* something */
}
catch( AError $e )
{
   handler1( $e );
}
catch ( BError $b )
{
   handler2( $e );
}
88 голосов
/ 02 мая 2016

Входит 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);
}
?>

функционально эквивалентны.

42 голосов
/ 30 мая 2016

Начиная с 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
    }
}
25 голосов
/ 16 января 2013

В этой статье рассматривается вопрос electrictoolbox.com / php-catch-множественные-исключения-типы . Содержание поста скопировано прямо из статьи:

Примеры исключений

Вот некоторые примеры исключений, которые были определены для целей этого примера:

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). Он не обязательно должен быть последним.

21 голосов
/ 05 июля 2012

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

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;

    }

}
5 голосов
/ 21 февраля 2014

Вот разумная альтернатива, если у вас нет контроля над определением исключений.Используйте имя переменной исключения, чтобы классифицировать исключения, когда они перехвачены.Затем проверьте наличие исключительной переменной после блока 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 много дублирования.

3 голосов
/ 18 апреля 2016

Помимо провала, можно также перейти с помощью 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

1 голос
/ 29 января 2016

Отличный способ - использовать 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'));
0 голосов
/ 19 июня 2018

Хм, есть много решений, написанных для php версии ниже 7.1.

Вот еще один простой способ для тех, кто не хочет перехватывать все исключения и не может создавать общие интерфейсы:

<?php
$ex = NULL
try {
    /* ... */
} catch (FirstException $ex) {
    // just do nothing here
} catch (SecondException $ex) {
    // just do nothing here
}
if ($ex !== NULL) {
    // handle those exceptions here!
}
?>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...