关于异常处理:如何在PHP中有效地使用try … catch块

How to efficiently use try…catch blocks in PHP

我在PHP代码中使用了try..catch块,但我不确定是否正确使用了它们。

例如,我的一些代码如下:

1
2
3
4
5
6
 try {
      $tableAresults = $dbHandler->doSomethingWithTableA();
      $tableBresults = $dbHandler->doSomethingElseWithTableB();
 } catch (Exception $e) {
      return $e;
 }

所以我将多个数据库操作分组在同一个try/catch块中,因为如果在任何事务中发生任何异常,我都可以处理它。

我这样做是因为我认为它比:

1
2
3
4
5
6
7
8
9
10
 try {
       $tableAresults = $dbHandler->doSomethingWithTableA();
 } catch (Exception $e) {
       return $e;
 }
 try {
       $tableBresults = $dbHandler->doSomethingWithTableB();
 } catch (Exception $e) {
       return $e;
 }

尽管如此,我不确定我所做的是一个好的实践还是一种懒惰的方法来捕获异常。

我的假设是,只有当异常需要特殊处理时,它才应该有自己的Try/Catch块,否则将它们分组到同一Try/Catch中应该是正常的。

所以我的问题是:

每个数据库事务使用try/catch块有什么好处吗?还是可以在同一个try/catch块中对多个数据库事务进行分组,而完全没有问题?

嵌套try/catch块可以吗?谢谢!

编辑

RETURN语句主要是为了演示,但我也在catch()中使用RETURNS,因为我对该方法发出了Ajax请求,而javascript期望得到一个JSON对象,那么如果发生异常,我将返回一个空的JSON编码数组。我只是认为在我的示例中放入特定的代码不会增加任何值。


重要注意事项

下面的讨论假设我们正在讨论的代码结构与上面的示例相同:无论选择哪种替代方法,异常都会导致方法在逻辑上停止执行中间的任何操作。

只要您打算做同样的事情,不管try块中的哪个语句抛出异常,那么最好使用单个try/catch。例如:

1
2
3
4
5
6
7
8
9
function createCar()
{
    try {
      install_engine();
      install_brakes();
    } catch (Exception $e) {
        die("I could not create a car");
    }
}

如果您能够并打算以特定于具体原因的方式处理故障,那么多个try/catch块非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function makeCocktail()
{
    try {
        pour_ingredients();
        stir();
    } catch (Exception $e) {
        die("I could not make you a cocktail");
    }

    try {
        put_decorative_umbrella();
    } catch (Exception $e) {
        echo"We 're out of umbrellas, but the drink itself is fine"
    }
}


为了子孙后代着想,答案可能太晚了。您应该检查变量的返回值并抛出异常。在这种情况下,您可以确保程序将从引发异常的位置跳转到catch块。在下面找到。

1
2
3
4
5
6
7
8
9
10
11
12
try{
   $tableAresults = $dbHandler->doSomethingWithTableA();
   if (!tableAresults)
     throw new Exception('Problem with tableAresults');

  $tableBresults = $dbHandler->doSomethingElseWithTableB();
   if (!tableBresults)
     throw new Exception('Problem with tableBresults');
} catch (Exception $e) {
    echo $e->getMessage();

}


在一个try-catch块中,它的可读性更高。如果重要的是识别一种错误,我建议您自定义异常。

1
2
3
4
5
6
7
8
try {
  $tableAresults = $dbHandler->doSomethingWithTableA();
  $tableBresults = $dbHandler->doSomethingElseWithTableB();
} catch (TableAException $e){
  throw $e;
} catch (Exception $e) {
  throw $e;
}

没有理由不将单个块用于多个操作,因为任何引发的异常都将阻止在失败的操作之后执行进一步的操作。至少只要您能断定哪个操作因捕获的异常而失败。只要不处理某些操作就可以。

不过,我想说,返回异常情况的意义有限。函数的返回值应该是某些操作的预期结果,而不是异常。如果您需要对调用范围中的异常作出反应,那么要么在函数内部的此处不捕获异常,而是在调用范围中捕获异常,要么在完成一些调试日志记录等之后重新抛出异常以供以后处理。


当抛出异常时,立即停止执行,并在catch{}块继续执行。这意味着,如果将数据库调用放在同一个try{}块中,而$tableAresults = $dbHandler->doSomethingWithTableA();抛出异常,则不会发生$tableBresults = $dbHandler->doSomethingElseWithTableB();。第二种选择是,当执行恢复时,$tableBresults = $dbHandler->doSomethingElseWithTableB();仍然会发生,因为它在catch{}块之后。

对于每种情况都没有理想的选择;如果您希望第二个操作无论如何都继续,则必须使用两个块。如果不执行第二个操作是可以接受的(或可取的),那么应该只使用一个操作。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try
{
    $tableAresults = $dbHandler->doSomethingWithTableA();
    if(!tableAresults)
    {
        throw new Exception('Problem with tableAresults');
    }
    $tableBresults = $dbHandler->doSomethingElseWithTableB();
    if(!tableBresults)
    {
        throw new Exception('Problem with tableBresults');
    }
} catch (Exception $e)
{
    echo $e->getMessage();
}


在一个try-catch块中,您可以做所有的事情,如果您希望不同的catch块中的错误显示为特定错误的消息,那么最佳实践是捕获它们。


用下面这样的一个try-catch块编写多行执行没有任何问题

1
2
3
4
5
6
7
try{
install_engine();
install_break();
}
catch(Exception $e){
show_exception($e->getMessage());
}

install_engineinstall_break函数中发生任何异常时,控制将被传递到catch函数。另一个建议是适当地吃你的例外。这意味着,最好不要编写die('Message'),而是适当地进行异常处理。您可以考虑在错误处理中使用die()函数,但在异常处理中不使用。

当应该使用多个try catch块时如果希望不同的代码块异常显示不同类型的异常,或者试图从catch块中抛出任何异常,可以考虑使用多个try catch块,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try{
    install_engine();
    install_break();
    }
    catch(Exception $e){
    show_exception($e->getMessage());
    }
try{
install_body();
paint_body();
install_interiour();
}
catch(Exception $e){
throw new exception('Body Makeover faield')
}

有关如何在不同情况下使用Try-Catch块的详细信息,请参阅我在php上的博客Try-Catch。