PHP 异常 VS Return 函数中的用法(最佳实践)

PHP Exceptions VS Return usage in Functions (best practice)

在我们的团队中,我们经常讨论函数内部异常的使用 VS 某种 return 数组,但我仍然不确定如何以正确的方式进行。那么让我们试着从头开始问这个问题:

何时使用异常 VS returning 数组,包含有关结果的所有信息?

示例 #1

function doAwesomeStuff($foo) {

    // prepare return values...
    $result = [
        'success' => true,
        'infoMessage' => '',
        'stuffDetails' => []
    ];

    // check params...
    if (!is_numeric($foo)) {
        $result['success'] = false;
        $result['infoMessage'] = 'Something went wrong...';
        return $result;
    }

    // do something else...
    if ($somethingElseWentWrong) {
        $result['success'] = false;
        $result['infoMessage'] = 'Something else went wrong...';
        return $result;
    }

    // add new data...
    $result['stuffDetails'] = [
        'foo' => 'bar'
    ];

    // done...
    return $result;
}

$foo = 42;
$awesomeStuff = doAwesomeStuff($foo);
if ($awesomeStuff['success'] === false) {
    echo $awesomeStuff['infoMessage'];
    // some error handling stuff...
}

// do some other awesome stuff
// ...

示例 2

function doAwesomeStuff($foo) {

    // prepare return value...
    $stuffDetails = [];

    // check params...
    if (!is_numeric($foo)) {
        throw new InvalidArgumentException('Something went wrong...');
    }

    // do something else...
    if ($somethingElseWentWrong) {
        throw new AnotherException('Something else went wrong...');
    }

    // add new data...
    $stuffDetails = [
        'foo' => 'bar'
    ];

    // done...
    return $stuffDetails;
}


try {

    $foo = 42;
    $awesomeStuff = doAwesomeStuff($foo);

    // do some other awesome stuff... 
    // ...without caring about error handling at this point (it's all done in catch blocks)

} catch InvalidArgumentException($e) {
    echo $e->getMessage();
    // some error handling stuff...
} catch AnotherException($e) {
    echo $e->getMessage();
    // some error handling stuff...
}

那么 "better" 错误处理方式是哪个版本,#1 还是#2?这只是一个品味问题,还是对这两个版本之一有真正的争论?

对于方法或函数,有一个简单的最佳实践 "rule" 告诉您在函数中有一个出口点。这已经是答案了吗,这意味着它将是 v2?

我非常感谢关于这个主题的所有想法:-)

我会推荐 #2 with Exceptions,因为如果你使用 return 值进行错误处理,你就不能真正将它用于它的实际用途,returning 东西。此外,您可以使用预定义的异常类型或创建新的单独异常-类 来区分异常并根据需要处理它们。

Whosebug 上的其他人已经写了关于异常或 Return Codes/Errors:

  • Which, and why, do you prefer Exceptions or Return codes?
  • Exceptions or error codes

多年前,当我读到它时,我得到了很多关于这个问题的信息。首先,“告诉,不要问”原则 (here and here) 以及 Robert C. Martin 编写的 Clean Code 的整个第 3 章和第 7 章之后。

有了这个,你可以实现一些好的东西来理解这个方法应该做什么,不应该做什么。

一个方法应该 return 只,而且只在询问或要求某事时。询问某事的方法的前缀为:

  • get; //混合
  • has; //布尔值
  • is; //布尔值
  • can。 //布尔值

或者,可以有单词 return(不需要作为前缀):

  • doSomethingAwesomeAndReturn。 //混合

名字create是个例外,因为创建不需要return东西:

  • createSomethingAwesomeAndReturn。 //混合

如果你的方法没有任何这些,它不应该 return 任何东西。

在第 3 章中,您可以找到名为 "Prefer Exceptions to Returning Error Codes" 的部分,在第 7 章中,您可以找到 "Don’t Return Null"。这解释了一些由 returning 产生的问题。大量验证链、空对象等...只需 return 异常即可解决这些问题。

你的第二个例子很好,但就像方法名称所说的那样,它做了很棒的事情,不应该 return。如果没有例外,它做了很棒的事情。如果确实需要 return,您必须重构或至少重命名您的方法以匹配预期。

毕竟,这只是指导你编码的很多建议,而不是死板的规则。

更新

我忘了前缀 add。这是一个例外。以 add 为前缀的方法应该 return 与 fluent interface 匹配的相同对象,没有别的。作为 Fluent Interface,它不应该与上述规则匹配,方法的 name/prefix/suffix 无关紧要,因为它只是为了流畅,而不是为了内聚。