PHP编码风格:在switch/case中使用return;

80

我们正在为团队实施新的编码风格指南,当在 switch case 语句中没有找到 "break" 时,php codesniffer 将会打印一个警告信息:

switch ($foo) {   
    case 1:
      return 1;   
    case 2:
      return 2;   
   default:
       return 3; 
}

有没有充分的理由使用“:

   switch ($foo) {
       case 1:
         return 1;
         break;
   }

为什么永远不会到达break语句?


我建议将@category问题作为一个独立的问题,因为它与原问题无关。 - John Carter
也许codesniffer只是错误的,并没有检查return,而只检查了break - Gumbo
7个回答

122

当你从switch语句中 return 时,可以不添加break,这是完全有效的。

但作为一种防御性编程实践,增加显式的break在每个case中是相当常见的做法。

switch ($foo) {
    case 1:
        return 1;
        break;

    case 2:
        return 2;
        break;
}

这个想法是,如果你之后在case1里改变代码并删掉了return语句,那么可能会忘记添加break

这会意外地导致程序流程落到case2中。

switch ($foo) {
    case 1:
        somethingDifferent();

    case 2:
        return 2;
        break;
}

通过 case 语句进行“穿透”略微不寻常,当你这样做时,应在代码中添加注释以表明这是有意为之的。

switch ($foo) {
    case 1:
        somethingDifferentAndWeWantToDoCase2AsWell();
        // fallthrough

    case 2:
        return 2;
        break;
}

正如许多防御性编程实践一样,你需要平衡代码膨胀的影响,也就是潜在地使代码混乱和难以阅读,是否值得这么做。


12

如果您的"php codesniffer正在打印警告",请尝试获取另一个更好的codesniffer,并不要忘记尝试使用最新的PHP稳定版本。当然,您可以在一个return后写一个break,但这没有意义,因为它根本不会被阅读。您的代码是正确的。

看这个:

$fun = function(int $argument): string {
    switch ($argument) {
        case 1:
            return "one";
        case 2:
            return "two";
        default:
            return "more than two";
    }
};
$str = $fun(4); // return "more than two"
在我看来,这种方式更简单更好:代码行数越少,维护起来就越轻松 :-)

8
回答你的问题,没有什么好的理由去拥有一个什么也不做的东西。这样想,用return后面的注释代替break来说“别忘了”将会产生相同的效果——没有任何影响。这样说起来听起来很傻,对吧?
除非你需要设置一个变量以便稍后使用,否则我建议你所采用的方法是完全可以的。我在看代码时两秒钟内就知道了它的意图。使用break只会造成混乱。
实际上,并没有一种大小适合所有情况的方法。正确的方法取决于哪个适合该场景。在每个case中设置一个变量并使用break可能是正确的方式,或者只返回也是有意义的。

 
 


关于其他答案中提出的一些观点:

1) return后没有break可能会导致以后代码发生问题

尽可能使代码明确,易读和清晰。我们还可以编写代码以使将来的更改更容易。但是,在像switch这样简单的情况下,重构稍后的case以添加或删除returnbreak应该不成问题。

实际上,如果您删除了一个return并且“没有注意到没有break”,那么这是一个错误,可能发生在编码的任何部分。没有什么能够拯救你。而且,编写未来潜力的代码时应该非常小心,因为那种潜力可能永远不会发生,或者其他事情可能会发生,您最终只需维护过时的代码多年。

同样,这也被认为是未来更改的安全网 - 如果您删除了return,并且意外留下了应该删除的安全网break呢?

即使这个switch语句是生死攸关的真正严肃的代码,我也反对在return后添加“无意义”的break。只要确保任何在代码上工作的人都知道他们在做什么,并且经过了足够多的代码审查和完整测试。
如果情况如此严峻,那么您将设置额外的检查,而不是提出用于捕捉粗心开发人员的建议性安全网。
主张在return之后添加break会增加安全保障,这意味着您没有正确编码或测试。如果这是被认为有用的安全保障,那么可能存在大量的漏洞在代码中,可能更为严重的地方。 “防御性编程”的维基百科文章被链接到,但这与本文无关: 防御式编程是一种旨在确保软件在不可预见情况下持续运行的防御性设计。
保留安全网中的break不是意外情况或防御性编程的情况。这只是糟糕的编码方式,而且你不能在代码中添加备用代码以防万一在更改某些内容时编码不正确。这是编码的糟糕方法。"如果有人删除了return它就无法工作"的论点,那么您也可能在case变量中拼写错误,或者忘记编写case,或...。 return返回,您不会进行"防御性"编码以避免返回失败。那意味着PHP出现了问题,而你不会在代码中添加安全网来应对这种情况。这是你在更高层次上所拥有的东西。
但是,在return之后添加break可以使其明确。但这是明显错误的。return返回,因此break不会发生。对我来说,这是令人困惑的时刻,我会想知道是否错过了意图,不过不会太久,因为很清楚将会发生什么,但是会有一个瞬间,我会思考一下以确保没有错过任何东西。
虽然在同一个case语句中使用return和break不是无效或错误的,但它完全没有意义,因为break什么也没做。这是毫无逻辑的无用代码,需要查看、维护和解决。
如果明确的目标是核心,并且在return后面添加break让你感到不爽,因为它毫无意义,那么我建议设置一个变量并使用break,然后在退出switch后返回该变量。
像@RageZ的回答https://dev59.com/6XM_5IYBdhLWcg3wTRLL#1437476所示。

 

3) 在 switch 语句完成后设置一个变量并返回

这种方法没有任何问题,但是如果没有必要将值存储在变量中(稍后使用等),那么当没有必要停留做其他事情时,立即返回是很好的。

这表明了明确的意图 - 在匹配到 case 后立即返回一个值。


3
我有更好的解决方案。请按照以下代码执行上述开关语句:
$result = 3; // for default case
switch ($foo) {   
    case 1:
      $result = 1;
      break;  
    case 2:
      $result = 2;
      break;    
   default:
      // do nothing
}
return $result;

这不会导致任何错误,代码与概念也很好。


2
在 switch 的 default 中不使用默认值会更好吗?我的意思并不是说这样做是“错误”的,但是 switch 有一个“default”,你可以声明为“什么也不做”,然后再用变量创建一个新的 default。在我看来,省略默认变量行并利用已经存在的 switch 可以使代码更加简洁。 - James

1

从 PHP 8.0 开始,你可以使用 match 表达式:

return match ($foo) {
    1 => 1,
    2 => 2,
    default => 3,
};

-4

我不是完美编码的专家,但我认为验证器会更喜欢这样的东西

switch ($foo) {   
    case 1:
      $ret =  1;   
      break;
    case 2:
      $ret = 2;
      break;   
   default:
       $ret = 3

}
return $ret

我认为在 case 语句中使用 return 来打破代码的流程并不是最佳实践。这就是为什么验证器会说没有 break 的原因…

至于你关于类别的问题,我不知道…抱歉


也许不是@therefromhere的首选,因为它消耗了“不必要”的属性...但当算法中已经存在(必需)$ret以执行某些操作时,这是一个不错的选择。 - Peter Krauss
在case语句中使用return来打破代码的流程并不是最佳实践,我持有异议。首先,“最佳实践”通常是模糊不清的,至少取决于代码库、场景等。但“良好的实践”是尽早返回。在这里,你只是设置了一个变量,稍后返回它,然后什么也没做。在每个case中使用return可以避免设置变量,并且少得多的代码需要查看。 - James

-15

来自PHP手册(http://us3.php.net/manual/en/control-structures.switch.php):

PHP会继续执行语句,直到switch块的末尾或第一次看到break语句。如果您在case语句列表的末尾不写break语句,则PHP将继续执行以下case的语句。例如:

<?php
switch ($i) {
    case 0:
        echo "i equals 0";
    case 1:
        echo "i equals 1";
    case 2:
        echo "i equals 2";
}
?>

在这里,如果 $i 等于 0,PHP 将执行所有的 echo 语句!如果 $i 等于 1,PHP 将执行最后两个 echo 语句。只有当 $i 等于 2 时,您才会得到预期的行为('i equals 2' 将被显示)。因此,在某些情况下,即使您想避免提供它们,也不要忘记 break 语句的重要性。


4
他在谈论从一个开关返回,你没有回答他的问题。 - John Carter
在这种情况下不相关。case块通过return退出。 - Gumbo
2
您是正确的 - 我没有仔细阅读问题。对此感到抱歉。 - Sander

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