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

我想用一种更干净的方式来获得以下功能,在一个块中捕获AErrorBError:

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

有什么办法可以做到吗?还是我得分别抓他们?

AErrorBerror有一个共享的基类,但它们也与其他类型共享,我想通过handler2来实现,所以我不能只捕获基类。

240722 次浏览

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

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


}


}

本文讨论的问题是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)块处理。它不一定是最后一个。

更新:

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

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 {}

你需要处理的不同于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这样的东西是hack,如果可能的话应该避免。

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

你可以

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

如果您无法控制异常的定义,这里有一个合理的替代方案。在捕获异常时,使用异常变量的名称对异常进行分类。然后在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块实现之间存在大量重复的情况下才有价值。

这里没有列出的另一个选项是使用异常的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
}
}

一个很好的方法是使用set_exception_handler

警告! !在PHP 7中,如果出现致命错误,可能会出现死机白屏。例如,如果你在一个非对象上调用一个方法,你通常会得到Fatal error: Call to a member function your_method() on null,如果错误报告是打开的,你会期望看到这个。

catch(Exception $e)将不会捕获上述错误。 上述错误将不会触发set_error_handler设置的任何自定义错误处理程序。< / p > 你必须在PHP7中使用catch(Error $e){ }来捕获错误。

class ErrorHandler{
public static function excep_handler($e)
{
print_r($e);
}
}
set_exception_handler(array('ErrorHandler','excep_handler'));
除了fall-through,还可以通过使用转到来跳过。 如果你想看到世界燃烧,这是非常有用的。

<?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

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

在功能上是等同的。

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

嗯,有很多解决方案是为低于7.1的php版本编写的。

下面是另一个简单的方法,适用于那些不想catch all异常且不能创建公共接口的人:

<?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!
}
?>

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

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