多个返回值表示成功/失败。

时间:2008-09-16 14:00:15

标签: php

我有兴趣获得一些关于我从某处获取的这种技术的反馈。

当函数可以成功或失败时,我会使用它,但是您希望获得有关失败原因的更多信息。执行相同操作的标准方法是使用异常处理,但我经常发现它有点超过这种情况,加上PHP4不提供此功能。

基本上,该技术涉及返回true表示成功, 等于表示失败。这是一个展示我的意思的例子:

define ('DUPLICATE_USERNAME', false);
define ('DATABASE_ERROR', 0);
define ('INSUFFICIENT_DETAILS', 0.0);
define ('OK', true);

function createUser($username) {
    // create the user and return the appropriate constant from the above
}

这样做的好处在于,在您的调用代码中,如果您不关心用户创建失败的原因,您可以编写简单易读的代码:

if (createUser('fred')) {
    // yay, it worked!
} else {
    // aww, it didn't work.
}

如果你特别想检查它为什么不起作用(用于记录,显示给用户或做任何事情),请使用与===

的身份比较
$status = createUser('fred');
if ($status) {
    // yay, it worked!
} else if ($status === DUPLICATE_USERNAME) {
    // tell the user about it and get them to try again.
} else {
    // aww, it didn't work. log it and show a generic error message? whatever.
}

我认为这样做的好处是,正常的期望是成功执行这样的函数会返回true,并且失败返回false。

缺点是你只能拥有7 "error" return values: false, 0, 0.0, "0", null, "", and (object) null.如果你忘记使用身份检查,你可能会让你的程序流程出错。其他人告诉我,使用enum这样的常量,它们都等于假是"ick"


所以,重申一个问题:这种做法有多接受?你会推荐一种不同的方式来实现同样的目的吗?

14 个答案:

答案 0 :(得分:13)

我同意其他已经说过WTFy方面的内容的人。如果它是明确记录的功能,那么它不是一个问题,但我认为采用替代路由返回0表示成功和整数表示错误代码更安全。如果您不喜欢这个想法或全局最后一个错误变量的想法,请考虑将您的函数重新定义为:

function createUser($username, &$error)

然后你可以使用:

if (createUser('fred', $error)) {
    echo 'success';
}
else {
    echo $error;
}

在createUser中,只需填充$ error就会遇到任何错误,并且由于引用,它可以在函数范围之外访问。

答案 1 :(得分:2)

只要记录和签约,并且没有太多WTFy,那么应该没有问题。

然后,我会建议使用例外来做这样的事情。这更有意义。如果你可以使用PHP5,那么这就是你要走的路。否则你没有太多选择。

答案 2 :(得分:2)

当异常不可用时,我看到的一种更常见的方法是将错误类型存储在某个地方的'last_error'变量中,然后在发生故障时(即返回false)查找错误。

另一种方法是使用古老的unix工具方法编号错误代码 - 为成功返回0,为各种错误条件返回任何整数(映射到某个错误)。

然而,当我看到它们被使用时,大多数都会遇到异常。

回应安德鲁的评论 - 我同意last_error不应该是全球性的,也许我答案中的“某处”有点模糊 - 其他人已经提出了更好的地方,所以我不打算重复它们

答案 3 :(得分:2)

通常,您将返回0表示成功,并返回1,2,3等表示不同的故障。你做这件事的方式有点像hackish,因为你只能有这么多的错误,这种编码迟早会咬你。

我喜欢定义一个包含布尔值的结构/对象来表示成功,错误消息或其他值表示发生了什么类型的错误。您还可以包含其他字段以指示执行了哪种操作。

这使得日志记录变得非常简单,因为您可以将status-struct传递给记录器,然后它将插入相应的日志条目。

答案 4 :(得分:2)

  

这种做法多么可以接受?

我说这是不可接受的。

  1. 需要===运算符,这非常危险。如果用户使用==,则会导致很难找到错误。
  2. 使用“0”和“”表示false可能会在将来的PHP版本中发生变化。此外,在许多其他语言中,“0”和“”不会评估为假,这会导致很大的混乱
  3. 使用getLastError()类型的全局函数可能是PHP中的最佳实践,因为它与语言很好地结合,因为PHP仍然主要是程序语言。我认为你刚才给出的方法的另一个问题是很少有其他系统像这样工作。程序员必须学习这种错误检查方式,这是错误的根源。最好让事情像大多数人期望的那样工作。

    if ( makeClient() )
    { // happy scenario goes here }
    
    else
    {
        // error handling all goes inside this block
        switch ( getMakeClientError() )
        { case: // .. }
    }
    

答案 5 :(得分:1)

当异常不可用时,我会使用PEAR模型并在所有类中提供isError()功能。

答案 6 :(得分:1)

在这里重新发明轮子。使用正方形。

好的,你在PHP 4中没有例外。欢迎在1982年,看看C。

您可以拥有错误代码。考虑负值,它们看起来更直观,所以你只需要检查是否(createUser()> 0)。

如果需要,您可以输入错误日志,并将错误消息(或任意错误代码)推送到阵列上,之后处理优雅。

但是PHP是一种松散类型的语言,出于某种原因,抛出具有不同类型但是评估为相同“错误”的错误代码是不应该做的事情。

当你用完内置类型时会发生什么?

当你得到一个新的编码器并且必须解释这个东西是如何工作的时候会发生什么?比如,在6个月内,你将不会记得。

PHP ===运算符是否足够快以通过它?它比错误代码更快吗?或任何其他方法?

放下它。

答案 7 :(得分:0)

伊克。

在Unix预例外中,这是用errno完成的。成功返回0或失败返回-1,然后您可以使用整数错误代码检索值以获取实际错误。这适用于所有情况,因为您对错误代码的数量没有(实际)限制。 INT_MAX肯定超过7,你不必担心类型(错误)。

我投票反对问题中提出的解决方案。

答案 8 :(得分:0)

如果你真的想做这种事情,你应该为每个错误设置不同的值,并检查是否成功。像

这样的东西
define ('OK', 0);
define ('DUPLICATE_USERNAME', 1);
define ('DATABASE_ERROR', 2);
define ('INSUFFICIENT_DETAILS', 3);

并检查:

if (createUser('fred') == OK) {
    //OK

}
else {
    //Fail
}

答案 9 :(得分:0)

成功执行返回true确实有意义。处理一般错误会更容易:

if (!createUser($username)) {
// the dingo ate my user.
// deal with it.
}

但将意义与不同类型的错误联系在一起根本没有意义。无论类型或编程语言如何处理,False都应该只涉及一件事和一件事。如果你要定义错误状态常量,最好坚持使用switch / case

define(DUPLICATE_USERNAME, 4)
define(USERNAME_NOT_ALPHANUM, 8)

switch ($status) {
case DUPLICATE_USERNAME:
  // sorry hun, there's someone else
  break;
case USERNAME_NOT_ALPHANUM:
  break;
default:
  // yay, it worked
}

使用这种技术,您将能够按位AND和OR状态消息,因此您可以返回带有多个含义的状态消息,如DUPLICATE_USERNAME & USERNAME_NOT_ALPHANUM,并对其进行适当处理。这并不总是一个好主意,这取决于你如何使用它。

答案 10 :(得分:0)

我喜欢COM处理异常和非异常调用者的方式。下面的示例显示了如何测试HRESULT并在发生故障时抛出异常。 (通常在tli文件中自动生成)

inline _bstr_t IMyClass::GetName ( ) {
    BSTR _result;
    HRESULT _hr = get_name(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _bstr_t(_result, false);
}

使用返回值会影响可读性,因为错误处理是分散的,最坏的情况是,代码永远不会检查返回值。这就是为什么我在违反合同时更喜欢例外。

答案 11 :(得分:0)

其他方式包括例外:

throw new Validation_Exception_SQLDuplicate("There's someone else, hun");),

返回结构,

return new Result($status, $stuff);
if ($result->status == 0) {
    $stuff = $result->data;
}
else {
    die('Oh hell');
}

我不愿意成为那些使用您最初建议的代码模式的人。

我的意思是“跟在你后面”,就像“在你的工作中跟随你并且不得不维持代码”而不是“跟随你”“用一个楔形”,尽管两者都是选择。

答案 12 :(得分:-1)

在我看来,只有当失败是你的方法/功能的“正常操作部分”时,才应该使用这种技术。例如,调用可能会因为失败而成功。如果失败是异常事件,那么您应该使用异常处理,以便您的程序尽可能早地和优雅地终止。

至于你使用不同的“假”值,我最好返回一个带有正确错误代码的自定义“Result”类的实例。类似的东西:

class Result
{
    var $_result;
    var $_errormsg;

    function Result($res, $error)
    {
       $this->_result = $res;
       $ths->_errorMsg = $error
    }

    function getResult()
    {
       return $this->_result;
    }

    function isError()
    {
       return ! ((boolean) $this->_result);
    }

    function getErrorMessage()
    {
       return $this->_errorMsg;
    }

答案 13 :(得分:-2)

查看COM HRESULT以获得正确的方法。

但例外通常更好。

更新:正确的方法是:根据需要定义尽可能多的错误值,而不仅仅是“假”错误值。使用function succeeded()来检查函数是否成功。

if (succeeded(result = MyFunction()))
  ...
else
  ...