代码之家  ›  专栏  ›  技术社区  ›  nickf

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

php
  •  5
  • nickf  · 技术社区  · 16 年前

    我有兴趣从某个地方得到一些关于这种技术的反馈。

    当一个函数可以成功或失败时,我会使用这个函数,但是您希望获得关于它失败原因的更多信息。做同样事情的一个标准方法是异常处理,但是我经常发现这类事情有点过头了,而且php4不提供这个。

    基本上,这项技术是为了成功而回归真实,以及 某物 哪一个 等值 以失败告终。下面是一个例子来说明我的意思:

    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 回复  |  直到 9 年前
        1
  •  13
  •   Jeremy Privett    16 年前

    我同意其他人的看法,他们说这是有点在wtfy方面。如果它有明确的文档功能,那么它就不是问题了,但是我认为采用另一种方法更安全:成功返回0,错误代码返回整数。如果您不喜欢这个想法或全局上一个错误变量的想法,请考虑将函数重新定义为:

    function createUser($username, &$error)
    

    然后您可以使用:

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

    在createuser中,只要用遇到的任何错误填充$error,就可以在函数范围之外访问它,因为引用。

        2
  •  2
  •   Chris Broadfoot    16 年前

    只要它有文件记录和合同,而且不太WTFY,那么就不会有问题。

    然后,我再次建议对类似的事情使用异常。这更有意义。如果你能使用php5,那么这就是你要做的事情。否则你就别无选择了。

        3
  •  2
  •   mmaibaum    16 年前

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

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

    当我看到它们被使用的时候,与异常相比,它们中的大多数都会受到影响。

    只是为了回应安德鲁的评论- 我同意最后一个错误不应该是全球性的,也许我的答案中的“某处”有点含糊——其他人已经提出了更好的地方,所以我不想重复它们。

        4
  •  2
  •   kizzx2    16 年前

    这种做法有多可接受?

    我认为这是不可接受的。

    1. 需要==运算符,这非常危险。如果用户使用==,则会导致很难找到错误。
    2. 在未来的PHP版本中,使用“0”和“”表示false可能会有所改变。另外,在许多其他语言中,“0”和“0”不会被评估为错误,这会导致很大的混乱。

    使用getLastError()类型的全局函数可能是PHP中的最佳实践,因为它 与语言很好地联系在一起 ,因为PHP仍然主要是一个过程语言。我认为您刚才给出的方法的另一个问题是,很少有其他系统能像这样工作。程序员必须学习这种错误检查方法,这是错误的根源。最好让事情像大多数人期望的那样运作。

    if ( makeClient() )
    { // happy scenario goes here }
    
    else
    {
        // error handling all goes inside this block
        switch ( getMakeClientError() )
        { case: // .. }
    }
    
        5
  •  2
  •   Peter Mortensen icecrime    11 年前

    通常返回0表示成功,返回1、2、3等表示不同的失败。你的方法有点黑客,因为你只能有这么多的错误,这种编码 迟早会咬你。

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

    这使得日志记录非常容易,因为您可以将状态结构传递到日志记录程序中,然后它将插入适当的日志条目。

        6
  •  1
  •   Plaster    16 年前

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

    好的,在PHP4中没有异常。欢迎来到1982年,看看C。

    您可以有错误代码。考虑负值,它们看起来更直观,因此您只需检查(createUser()>0)。

    如果需要的话,您可以有一个错误日志,将错误消息(或者只是任意的错误代码)推到一个数组上,然后处理优雅的问题。

    但是,出于某种原因,PHP是一种松散类型的语言,而抛出具有不同类型但计算结果相同的错误代码是不应该做的事情。

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

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

    php==operator足够快吗?它比错误代码快吗?或者其他方法?

    把它扔了。

        7
  •  1
  •   Peter Mortensen icecrime    11 年前

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

        8
  •  0
  •   Jonathan Adelson    16 年前

    艾克。

    在unix pre-exception中,这是用errno完成的。如果成功返回0或失败返回-1,则可以使用整数错误代码检索值以获取实际错误。这在所有情况下都有效,因为您对错误代码的数量没有(实际的)限制。int-max肯定大于7,您不必担心类型(errno)。

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

        9
  •  0
  •   rami    16 年前

    成功的执行返回true是有意义的。处理一般性错误要容易得多:

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

    但是把意义和不同类型的错误联系起来根本没有意义。“假”应该只意味着一件事和一件事,而不管编程语言如何处理它。如果无论如何都要定义错误状态常量,最好还是使用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
    }
    

    同样,使用这种技术,您将能够按位和或状态消息,这样您就可以返回包含多个含义的状态消息,比如 DUPLICATE_USERNAME & USERNAME_NOT_ALPHANUM 并妥善处理。这并不总是一个好主意,这取决于你如何使用它。

        10
  •  0
  •   Markowitch    16 年前

    我喜欢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
  •   Peter Mortensen icecrime    11 年前

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

    define ('OK', 0);
    define ('DUPLICATE_USERNAME', 1);
    define ('DATABASE_ERROR', 2);
    define ('INSUFFICIENT_DETAILS', 3);
    

    检查:

    if (createUser('fred') == OK) {
        //OK
    
    }
    else {
        //Fail
    }
    
        12
  •  0
  •   Peter Mortensen icecrime    11 年前

    其他方式包括例外:

    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');
    }
    

    我不想成为那个因为使用了你最初建议的代码模式而来找你的人。

    我的意思是“来找你”,就像“在工作中跟踪你”,并且必须保持“而不是”跟在你后面“和一个妻子”,尽管两者都是选择。

        13
  •  -1
  •   Peter Mortensen icecrime    11 年前

    在我看来,只有当失败是方法/函数的“正常操作部分”时,才应该使用这种技术。例如,呼叫成功的可能性和失败的可能性一样大。如果失败是一个异常事件,那么您应该使用异常处理,这样您的程序就可以尽早、优雅地终止。

    至于您使用不同的“false”值,我最好返回一个带有正确错误代码的自定义“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;
        }
    
        14
  •  -2
  •   ima    16 年前

    查看com-hresult以了解正确的方法。

    但例外情况通常更好。

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

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