【问题标题】:Catching multiple exception types in one catch block在一个 catch 块中捕获多种异常类型
【发布时间】:2012-01-16 09:31:11
【问题描述】:

我想要一种更简洁的方法来获得以下功能,以便在一个块中捕获 AErrorBError

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

有没有办法做到这一点?还是必须分开抓?

AErrorBerror 有一个共享的基类,但它们也与其他类型共享它,我想将其归入handler2,所以我不能只抓住基类。

【问题讨论】:

标签: php exception-handling


【解决方案1】:

更新:

从 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-294

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
}

如您所见herehere,即使是SPL 默认异常也有一个可以利用的层次结构。另外,如PHP Manual中所述:

当抛出异常时,语句后面的代码不会 执行,PHP 将尝试找到第一个匹配的 catch 块。

这意味着你也可以拥有

class CError extends LetterError {}

您需要以不同于AErrorBError 的方式处理它,因此您的catch 语句如下所示:

catch(CError $e){
    //voodoo
}
catch(LetterError $e){
    //voodoo
}

如果您遇到的情况是,有 20 个或更多异常合法地属于同一个超类,并且您需要以一种方式处理其中的 5 个(或任何大型组),而其余的则以另一种方式处理,您仍然可以这样做。

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

然后:

catch (Group1 $e) {}

在处理异常时使用 OOP 非常强大。使用 get_classinstanceof 之类的东西是黑客行为,应尽可能避免使用。

我想添加的另一个解决方案是将异常处理功能放在它自己的方法中。

你可以有

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 的一般结构中工作。

【讨论】:

  • 这是正确答案的另一票。不幸的是,诸如在接受的答案中所说的内容以及它被接受为正确答案的事实,正是使 PHP 变得如此疯狂的原因。
  • 这应该是公认的答案。虽然,它假定您能够修改文件。 AError 可以在第三方更新的库/文件中实现。
  • @WaffleStealer654 即使您不能直接编辑文件,您仍然可以子类化文件并使它们实现您的组。假设您可以抛出异常,但您可以包装最基本的机制,异常将被抛出,然后捕获它并抛出您的包装异常。
  • 这不是公认的答案,因为当您使用 3rd 方库时,您不能这样做。
  • @DenisV 请参阅我上面的评论。它一直在企业软件中完成。封装很棒。
【解决方案2】:

在 PHP >= 7.1 中这是可能的。看到这个answer


如果可以修改异常,use this answer

如果不能,可以尝试使用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;
    }
}

但是use multiple catch blocks as described in aforementioned answer 可能会更好。

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

【讨论】:

  • 这就是我害怕的。如果有许多错误类型需要一起处理,那么将它们一起捕获并测试类型会很好,但是对于只有 2 个,例如在我的情况下,单独捕获它们可能更干净。谢谢!
  • @DominicGurto: 是的,我也同意 :) 我更关心 PHP 对 finally 声明的态度。 ;)
  • 但是不要忘记这会捕获所有异常,因此如果两者不匹配,则应该有类似... } else { throw($e); } 的内容。抱歉,可能语法错误,有一段时间没看到 php。
  • 如果您在这里阅读第一段:php.net/manual/en/language.exceptions.php,您将看到多个 catch 块是可能的并且是完全有效的解决方案。尽管 OP 错误地将两个异常类放在一个 catch 语句中。我认为最好用另一个包含多个 catch 块的示例来更新您的答案。
  • 建议一个解决所有其他异常的解决方案,根本不应该被接受......
【解决方案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 || $ex instanceof BError){
            //handle AError and BError
        } elseif($ex instanceof CError){
            //handle CError
        } else {
           throw $ex; // an unknown exception occurred, 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) 块处理。它不一定是最后一个。

      【讨论】:

      • 当您必须为两个或多个不同的异常执行相同的代码时,就会出现这种方法的问题。
      • 这是从 Electric Toolbox 检索到的。编辑帖子以表扬。
      • 使用 PHP 7.x,您需要 catch (Throwable $e) 来捕获所有异常。另见:php.net/manual/en/class.throwable.php
      【解决方案6】:

      作为已接受答案的扩展,您可以切换 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;
      
          }
      
      }
      

      【讨论】:

      • 使用多个 catch 代替此解决方案。
      【解决方案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】:

          嗯,有很多针对低于 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!
          }
          ?>
          

          【讨论】:

            【解决方案10】:

            从 PHP 8.0 开始,当您不需要输出错误内容(来自变量 $e)时,您可以使用更简洁的方法来捕获异常。但是,您必须将默认的 Exception 替换为 Throwable

            try {
                /* something */
            } catch (AError | BError) {
                handler1()
            } catch (Throwable) {
                handler2()
            }
            

            【讨论】:

            • 这也可以在 php >= 7.1 版本中完成。
            • 不行,以前的版本必须写变量名AError $e
            • 哦,你跳过了捕获错误。是的,这是 8.0 中的新功能。谢谢。
            【解决方案11】:

            此处未列出的另一个选项是使用异常的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 创建新的异常类?
            • 知道了。这就是我的解决方案的重点,您不需要创建任意类只是为了建立一个命名空间来引发特定的异常。我确定这就是他们添加指定代码功能的原因。
            • 我也没有投反对票,但我猜反对票的人认为这并不能回答问题。我建议以让读者清楚已经理解问题并且您仍然想为代码流提出一种完全不同的方式来开始回答。这个答案实际上并没有回答“如何捕获多个异常类型”,而是“如何处理异常的多个不同原因”。
            【解决方案12】:

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

            【讨论】:

            猜你喜欢
            • 2010-09-08
            • 2015-12-14
            • 2011-07-30
            • 1970-01-01
            • 2012-06-04
            • 1970-01-01
            • 2013-11-15
            • 1970-01-01
            相关资源
            最近更新 更多