在一个catch块中捕获多种异常类型


242

我想要一种更干净的方式来获得以下功能,AError并且BError可以一网打尽:

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

有什么办法吗?还是我必须分开抓住它们?

AErrorBerror具有一个共享的基类,但它们也与其他我要介绍的类型共享它handler2,所以我不能只抓住基类。


7
仅作为补充说明:已提交RFC,以捕获多个异常。让我们看一下此功能是否已成为PHP语言的一种方法... wiki.php.net/rfc/multiple-catch
SimonSimCity

10
^此功能在PHP 7.1中实现
李苏滨

Answers:


349

更新:

从PHP 7.1开始,此功能可用。

语法为:

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

文件:https//www.php.net/manual/en/language.exceptions.php#example-287

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

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


对于7.1之前的PHP:

尽管这些什么其他的答案说,你可以赶上AErrorBError在同一个块(这是比较容易,如果你是一个定义例外)。即使存在您要“掉线”的例外情况,您仍然应该能够定义层次结构来满足您的需求。

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

然后:

catch(LetterError $e){
    //voodoo
}

正如您在此处此处所看到的,即使SPL默认异常也具有可以利用的层次结构。另外,如PHP手册中所述

引发异常时,将不执行该语句之后的代码,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) {}

涉及异常时使用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);
}

这样,如果您的异常处理机制需要更改,并且您仍在OOP的常规构造中工作,则您仅需修改一个代码位置。


4
这是正确的答案。不幸的是,诸如PHP所接受的答案之类的东西以及它被接受为正确答案这一事实使PHP变得如此疯狂。
borfast 2014年

这应该是公认的答案。虽然,它假定您能够修改文件。AError可以在由第三方更新的库/文件中实现。
凯拉2014年

@ WaffleStealer654即使您不能直接编辑文件,您仍然可以将文件子类化并使其实现您的组。那将假定您可以抛出异常,但是您可以只包装最基础的机制,在该机制中将抛出异常,然后捕获该异常并抛出已包装的异常。
MirroredFate 2014年

3
这不是可接受的答案,因为使用第三方库时您不能这样做。
Denis V

@DenisV在您的上方查看我的评论。它始终在企业软件中完成。封装很棒。
MirroredFate 2014年

229

在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;
    }
}

但是,最好使用前面提到的答案中所述的多个catch块

try
{
    /* something */
}
catch( AError $e )
{
   handler1( $e );
}
catch ( BError $b )
{
   handler2( $e );
}

6
那就是我所担心的。如果需要将许多错误类型一起处理,则将它们收集在一起并测试类型将是很好的选择,但是对于仅两种错误类型(例如,在我的情况下),将它们分别捕获可能会更干净。谢谢!
Dominic Gurto 2011年

3
@DominicGurto:是的,我也赞成:)我会更加关注PHP对finally声明的态度。;)
亚历克斯

7
但是请不要忘记这会捕获所有异常,因此应该有一些... } else { throw($e); }不匹配的地方。抱歉,语法错误,没有看到php。
达里波·菲卢斯(Dallibor Filus)2011年

11
如果您在此处阅读第一段:php.net/manual/en/language.exceptions.php,您将看到可能有多个catch块,并且是完全有效的解决方案。但是,OP错误地将两个异常类放在一个catch语句中。我认为最好用另一个包含多个catch块的示例来更新您的答案。
Haralan Dobrev

4
建议解决所有其他异常的解决方案,根本不应该接受……
Stivni 2014年

88

即将在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);
}
?>

在功能上是等效的。


45

从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

本文涵盖了问题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)块处理。它不一定必须是最后一个。


3
当您必须为两个或更多不同的异常执行相同的代码时,就会出现此方法的问题。
Parziphal

这是从Electric Toolbox检索的。编辑帖子以功劳。
凯拉2014年

使用PHP 7.x,您需要catch (Throwable $e)捕获所有异常。另请参阅:php.net/manual/en/class.throwable.php
Mikko Rantalainen

21

作为已接受答案的扩展,您可以切换Exception的类型,从而产生与原始示例有点相似的模式:

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;

    }

}

6
使用多个捕获代替此解决方案。
亚历杭德罗·莫雷诺2014年

5

如果您无法控制定义异常,这是一个合理的选择。捕获异常时,请使用异常变量的名称对异常进行分类。然后在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

除了掉线之外,还可以使用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

一个很好的方法是使用 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'));

1
...或者您可以编写catch (Throwable $e) { ... }并完成它。另请参见:php.net/manual/en/class.throwable.php
Mikko Rantalainen

0

此处未列出的另一个选项是使用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
    }
}

我没有拒绝投票,但也许OOP纯粹主义者对您没有使用extends \Exception?创建新的异常类感到愤怒。
keyboardSmasher

得到它了。这就是我的解决方案的全部重点,您无需创建任意类就可以建立一个命名空间来引发特定的异常。我确定这就是为什么他们添加了指定代码的功能。
Mike Purcell

我也没有投票,但我想投票者认为这不能回答问题。我建议用一些让读者清楚的东西开始答案已经理解了这个问题,并且您仍然希望提出一种完全不同的代码流方式。这个答案实际上并没有回答“如何捕获多个异常类型 ”,而是“如何处理导致异常的多种不同原因”。
米科·兰塔莱宁

0

嗯,有很多针对7.1以下版本的php编写的解决方案。

对于那些不想捕获所有异常并且无法建立通用接口的人来说,这是另一种简单的方法:

<?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!
}
?>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.