PHP:检查函数是否应该抛出异常


PHP: Should a checking function throw an exception

这不一定只适用于PHP,但这是我关注的领域。

我最近写了一些检查函数,它们得到了一些参数,然后用各种方式检查它的有效性。类似地,checkXmlString($xml)将检查给定字符串是否包含格式良好的xml文档等。

问题是,这些函数是应该返回布尔值,还是抛出异常而在成功时不返回任何值。

所以

function checkAbc($arg) { if ($arg is invalid) return false; else return true; }

或者更确切地说

function checkAbc($arg) { if ($arg is invalid) throw new Exception(...); }

您可以抛出InvalidArgumentException来检查参数是否不正确,但我认为对于您的情况,如果您正在编写"checkers",它们应该返回一个布尔值,这样您就知道不要继续操作,例如,如果foobar.xml实际上是一个CSV文件,您不想继续操作,但也不想捕获一个exeption

<?php 
class Checker {
    function validXml($string)
    {
        if(!(bool)$string) throw new 'InvalidArgumentException("Cannot pass empty string as argument", 1);
        // Check
        // Is valid XML ? Return True : return false
    }
}
try {
    if(new Checker->validXml($xmlString))
    {
        // Continue Operation
        // return 
    }
    // Notify User of invalidity
    // return
} catch ('InvalidArgumentException $e) {
    // Log args 
    // 
}

根据几乎所有关于该主题的书籍,尤其是根据逻辑,名称应该是函数功能的最大提示。在这种情况下,只有第一个选项适用。正如Bet Lamed之前所说,一个名为"check"的函数不应该抛出异常,只是为了让你知道检查是否正常。

如果您想要异常,您可能需要将其重命名为DeserializationToAbc()或TryParseAbc()或类似的名称。

问题是,这些函数应该返回布尔值,还是抛出例外,成功后不返回任何信息。

首先确定是否是异常情况,而不是使用异常。您的情况似乎不是异常,这只是一种情况,所以将其视为一种情况。如果它是正确的,但在某些情况下失败了,那么您可能无法很好地识别,而您可能会考虑使用异常。

访问这两个链接并了解有关异常的更多信息

PHP 5.3 中的异常最佳实践

PHP异常入门

这当然有点基于观点,但问问自己,你对checkEmail()这样的函数有什么期望?该方法的目的是验证某些东西,因此您可以预期这个问题的答案。

$isValid = checkEmail($arg);

我认为大多数开发人员都希望得到一个bool作为返回值,它使代码可读。应该有错误的值,所以如果传递了无效的参数,就不能说这是一个异常。为了返回错误消息,我会使用out参数:

function checkAbc($arg, &$errorMessage)
{
  if ($arg is invalid)
  {
    $errorMessage = 'The argument is invalid because of...';
    return false;
  }
  else
  {
    $errorMessage = '';
    return true;
  }
}

我真的不太确定哪种形式通常更可取。

一方面,在出现错误的情况下,您想要一条有用的消息,因此最终会出现混合返回(true/string),这很难看,或者返回一个数组(甚至更难看)。一个例外是免费的。

另一方面,一个名为check的函数。。。()不应该引发异常,因为发现"这不是一个有效的东西"并不是什么异常,也不是错误。

第三种方法是称之为"throwIfFalse",但这也很丑陋。。。。

嗯。。。。

一种可能的解决方案:

interface Checker {
  public function check();
  public function getMessage();
}
class WhateverChecker implements Checker { ... }
class ClientOfChecker {
  public function doStuff() {
     $checker = new Checker();
     if (! $checker->check() )
        throw new Exception($checker->getMessage());
  }
}

然而,这似乎非常冗长,我可以说,对我来说,这是Java风格的。

关于您的问题,通常有两种类型的函数:

  • 测试条件是否成立的纯函数。

  • 确保条件成立并在不成立时更改控制流的功能。

不同的编程语言可能对这两种类型的命名有不同的约定。以C++为例,一种常见的命名是类型2的CHECK_XXX和类型1的IsXXX。下面是一个来自谷歌日志库教程的例子:

CHECK(fp->Write(x) == 4) << "Write failed!";
CHECK_NE(1, 2) << ": The world must be ending!";

另一个例子是Vimscript的maktaba实用程序库,其中maktaba#value#IsXXX()用于测试参数是否为特定类型,而maktaba#ensure#IsXXX()用于确保IsXXX保持并抛出异常。

function! TakeAString(name)
  " Ensure argument type is String.
  let name = maktaba#ensure#IsString(a:name)
endfunction
if maktaba#value#IsString(name)
  " Branch if name is a String.
  echo name
endif

所以重点是:选择最适合您需要的函数,并根据语言惯例命名函数。就两者的用例而言,大致上,使用类型2来检查类似前置条件的参数类型,并在条件语句中使用类型1。