多个返回值指示成功/失败。
我对从某处获得的关于此技术的一些反馈很感兴趣。
当函数可以成功或者失败时,我会用到它,但是我们想获得有关失败原因的更多信息。执行此操作的标准方法是使用异常处理,但是对于这种事情,我经常发现它有点过头,而且PHP4不提供此功能。
基本上,该技术涉及成功返回true,而失败则等同于false。这是一个例子来说明我的意思:
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个"错误"返回值:false,0、0.0," 0",null,""和(对象)null。流都错了。有人告诉我,使用像" enum"这样的常量都等于" false"的常量是"" ick"。
因此,重申一下这个问题:这样的做法是否可以接受?我们会建议采用其他方式来实现同一目标吗?
解决方案
回答
只要它被记录在案并签订合同,并且不要太WTFy,那么就不会有问题。
再说一次,我建议对这种情况使用例外。这更有意义。如果我们可以使用PHP5,那将是最佳选择。否则,我们别无选择。
回答
当异常不可用时,我看到的一种更常见的方法是将错误类型存储在某个地方的" last_error"变量中,然后在发生故障(即返回false)时查找错误。
另一种方法是使用古老的unix工具方法,编号为成功的错误代码返回0,而对于各种错误情况,则返回任何整数(映射到某些错误)。
与大多数例外相比,其中的大多数遭受了痛苦,而我已经看到它们被使用了。
只是为了回应安德鲁的评论
我同意last_error不应是全球性的,也许我回答中的"某个地方"有点模糊,其他人已经建议了更好的地方,所以我不会再重复这些了
回答
通常,我们将返回0表示成功,而返回1,2,3等,以表示不同的失败。操作方式有点怪异,因为我们只能有那么多错误,而且这种编码迟早会给我们带来麻烦。
我喜欢定义一个结构/对象,该结构/对象包括一个表示成功的布尔值,以及一个错误消息或者其他值来指示发生了哪种错误。我们还可以包括其他字段以指示执行了哪种操作。
这使记录变得非常容易,因为我们可以将状态结构传递到记录器中,然后将其插入适当的日志条目。
回答
我认为,仅当故障是方法/功能的"正常操作部分"时,才应使用此技术。例如,呼叫成功与失败的可能性一样大。如果失败是异常事件,则应使用异常处理,以便程序可以尽早而优雅地终止。
至于我们使用不同的"假"值,我最好返回带有适当错误代码的自定义"结果"类的实例。就像是:
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; }
回答
ck
在Unix前置例外中,这是通过errno完成的。返回0表示成功,返回-1表示失败,然后我们可以获取一个值,该值可以使用整数错误代码进行检索以获取实际错误。这在所有情况下都有效,因为我们没有(实际)错误代码数量的限制。 INT_MAX肯定大于7,并且我们不必担心类型(errno)。
我对问题中提出的解决方案投反对票。
回答
查看COM HRESULT,了解执行此操作的正确方法。
但是例外通常更好。
更新:正确的方法是:定义尽可能多的错误值,而不仅仅是"假"的。使用函数successed()来检查函数是否成功。
if (succeeded(result = MyFunction())) ... else ...
回答
我同意其他人所说的,这在WTFy方面有点。如果明确记录了功能,那么问题就不那么多了,但是我认为采用另一种方法比较安全,即成功返回0,错误代码返回整数。如果我们不喜欢这种想法,也不喜欢全局最后一个错误变量的想法,请考虑将函数重新定义为:
function createUser($username, &$error)
然后,我们可以使用:
if (createUser('fred', $error)) { echo 'success'; } else { echo $error; }
在createUser内部,只需用遇到的任何错误填充$ error,由于引用,它可以在函数范围之外访问。
回答
如果我们确实想做这种事情,则每个错误应该有不同的值,并检查是否成功。就像是
define ('OK', 0); define ('DUPLICATE_USERNAME', 1); define ('DATABASE_ERROR', 2); define ('INSUFFICIENT_DETAILS', 3);
并检查:
if (createUser('fred') == OK) { //OK } else { //Fail }
回答
成功执行返回true确实有意义。处理一般错误会容易得多:
if (!createUser($username)) { // the dingo ate my user. // deal with it. }
但是将含义与不同类型的错误相关联根本没有任何意义。 False意味着一件事和一件事,无论其类型或者编程语言如何对待它。如果仍要定义错误状态常量,最好坚持使用开关/外壳
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
并对其进行适当处理。这并不总是一个好主意,它取决于使用方式。
回答
how acceptable is a practice like this?
我会说这是不可接受的。
- 需要===运算符,这是非常危险的。如果用户使用==,则将导致非常难以发现错误。
- 在将来的PHP版本中,使用" 0"和""表示false可能会发生变化。另外,在许多其他语言中," 0"和""不会计算为false,这会导致很大的混乱
在PHP中使用getLastError()类型的全局函数可能是最佳实践,因为它与语言紧密相关,因为PHP仍然主要是一种过程语言。我认为我们刚刚采用的方法存在的另一个问题是,很少有其他系统能像这样工作。程序员必须学习这种错误检查方式,这是错误的根源。最好使事情像大多数人所期望的那样工作。
if ( makeClient() ) { // happy scenario goes here } else { // error handling all goes inside this block switch ( getMakeClientError() ) { case: // .. } }
回答
当没有例外时,我将使用PEAR模型并在所有类中提供isError()功能。
回答
我喜欢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); }
使用返回值将分散错误处理,并且在最坏的情况下会影响可读性,并且代码永远不会检查返回值。这就是为什么我更喜欢合同违约时的例外。
回答
其他方式包括例外:
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'); }
我不愿意成为使用我们最初建议的代码模式的人。
我的意思是"跟着你来",就像"跟着你去工作并必须遵守守则",而不是"带着恶意地跟着你来",尽管两者都是可以选择的。
回答
在这里重新发明轮子。使用正方形。
好的,我们在PHP 4中没有例外。1982年欢迎我们,来看看C。
我们可以有错误代码。考虑负值,它们看起来更直观,因此我们只需要检查(createUser()> 0)。
如果需要,可以有一个错误日志,将错误消息(或者只是任意错误代码)推送到数组上,然后再进行处理。
但是PHP是一种松散类型的语言,这是有原因的,并且不应抛出具有不同类型但求值为相同" false"的错误代码。
当我们用尽内置类型时会发生什么?
当我们有了新的编码器并必须解释这件事如何工作时会发生什么?说,在6个月内,我们将不会记得。
PHP ===运算符是否足够快才能通过?它比错误代码快吗?或者其他任何方法?
放下