如何在PHP中高效使用try...catch块

80

我一直在我的PHP代码中使用try...catch块,但我不确定是否使用得正确。

例如,我的一些代码看起来像这样:

 try {
      $tableAresults = $dbHandler->doSomethingWithTableA();
      $tableBresults = $dbHandler->doSomethingElseWithTableB();
 } catch (Exception $e) {
      return $e;
 }

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

我这样做是因为我认为这比以下写法更易读和高效:

 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块是否可行?

编辑

返回语句主要是为了演示目的,但我也在catch()中使用返回语句,因为我正在对该方法进行AJAX请求,而Javascript期望一个JSON对象,如果发生异常,则返回一个空的JSON编码数组。我只是认为在示例中放置特定代码不会添加任何价值。


我有相同的答案。这取决于!DB事务是否相互依赖,是否涉及FOREIGN KEYS?B表中的事务是否依赖于A表中的事务?这就是你需要自己问自己的问题,以决定是否可以将它们分组。 - CodeAngry
是的,表B中的事务依赖于表A中的事务。因此,如果事务A失败,则不应运行事务B。它可以运行,但无论如何都会返回一个空结果集。 - ILikeTacos
7
那就将它们分组!但要记得,你需要理解自己在做什么以及为什么要这么做。可以这样思考...一旦我执行某个操作,下一行代码是否需要执行?如果不需要,就把它们放在try{}块中。那些需要执行的...则在catch(){}结束后继续执行。 - CodeAngry
8个回答

93

重要提示

以下讨论假设我们正在谈论像上面示例中结构化的代码:无论选择哪种替代方案,异常都会导致方法在执行到一半时逻辑上停止。


只要您打算无论 try 代码块中的哪个语句抛出异常都执行相同的操作,那么使用单个 try/catch 显然更好。例如:

function createCar()
{
    try {
      install_engine();
      install_brakes();
    } catch (Exception $e) {
        die("I could not create a car");
    }
}

如果您能并打算根据具体原因处理失败,多个try/catch块是很有用的。

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

5
请不要忘记,PHP5.5增加了对finally的支持。 - Mike Mackintosh

21

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

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();

}

10

使用单个 try-catch 块可以使代码更易读。如果重要的话,我建议自定义异常以标识错误类型。

try {
  $tableAresults = $dbHandler->doSomethingWithTableA();
  $tableBresults = $dbHandler->doSomethingElseWithTableB();
} catch (TableAException $e){
  throw $e;
} catch (Exception $e) {
  throw $e;
}

7

使用单个块进行多个操作没有任何问题,因为任何抛出的异常都会阻止在失败操作后继续执行其他操作。只要你能从捕获的异常中得出哪个操作失败了,那么就可以这样做。也就是说,只要某些操作未经处理也没关系。

然而,我认为返回异常仅有限制性意义。函数的返回值应该是某些操作的预期结果,而不是异常。如果你需要在调用范围内对异常作出反应,则不要在函数内部捕获异常,而是在调用范围内捕获异常或重新抛出异常以供稍后处理,并进行一些调试日志记录等操作。


6
当抛出异常时,立即停止执行并继续进行 catch{} 块。这意味着,如果您将数据库调用放在同一个 try{} 块中,并且 $tableAresults = $dbHandler->doSomethingWithTableA(); 抛出异常,则 $tableBresults = $dbHandler->doSomethingElseWithTableB(); 将不会发生。对于第二个选项,$tableBresults = $dbHandler->doSomethingElseWithTableB(); 仍将发生,因为它是在 catch{} 块之后,当执行已经恢复的情况下。
对于每种情况都没有理想的选择;如果您希望第二个操作继续进行,则必须使用两个块。如果可以接受(或期望)第二个操作不会发生,则应仅使用一个块。

1
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();
}

1
在一个 try catch 块中,您可以完成所有操作。最佳实践是为不同的错误捕获它们自己的 catch 块,以便它们显示特定错误的消息。

0

在单个try catch块中编写多行执行是没有任何问题的,就像下面这样

try{
install_engine();
install_break();
}
catch(Exception $e){
show_exception($e->getMessage());
}

无论是在install_engine函数还是install_break函数中发生任何异常,控制权都将传递到catch函数。 另一个建议是正确处理异常。这意味着,与其编写die('Message'),最好是正确处理异常。您可以考虑在错误处理中使用die()函数,但不要在异常处理中使用。
当您应该使用多个try catch块 如果您希望不同的代码块异常显示不同类型的异常,或者您正在尝试从catch块中抛出任何异常,则可以考虑使用多个try catch块,如下所示:
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')
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接