【问题标题】:PHP: Should a checking function throw an exceptionPHP:检查函数是否应该抛出异常
【发布时间】:2015-11-11 11:39:42
【问题描述】:

这不一定只适用于 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(...); }

【问题讨论】:

  • 我会重命名为 isXmlString 并返回布尔值...,因为它的检查函数抛出异常似乎很奇怪,如果检查有问题,你会抛出这个。

标签: php exception error-checking


【解决方案1】:

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

如果您需要例外,您可能需要将其重命名为 DeserialisationToAbc() 或 TryParseAbc() 或类似名称。

【讨论】:

  • 当然,我的意思是公共接口很可能应该像 parseXml() throws WhatException 但是如果这些检查变得更广泛,那么您将在顶部有很多代码parseXml() 将其弄乱并降低了函数的可读性,因此您希望将检查代码与 ... ahm ....“实际做事”代码分开,然后您返回返回值以表示有效性.
【解决方案2】:

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

首先确定 If It is an exception 比使用 exception 。你的情况似乎不是异常的,它只是一个条件,所以把它当作一个条件。如果它是正确的,但在某些情况下失败,您可能无法比您考虑使用异常更好地识别。

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

Exception Best Practices in PHP 5.3

A primer on PHP exceptions

【讨论】:

  • "。你的情况似乎并不例外,这只是一个条件"不要太哲学化,但它有点奇怪的组合。您很可能只对您认为正确的事物使用检查功能,因此不正确是一种例外情况;但检查器函数并不真正知道这一点,所以它应该返回一个布尔值。
  • 不正确也不例外,如果你清楚什么时候会得到布尔假。如果您在获取布尔值 false 时不确定条件,则可以将其视为异常情况。
【解决方案3】:

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

<?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 
    // 
}

【讨论】:

    【解决方案4】:

    这当然有点基于意见,但问问你自己,对像 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;
      }
    }
    

    【讨论】:

    • 是的,这可能是一个很好的解决方案。有趣的是,我几乎从不使用输出参数,所以我什至没有想到...
    • @BetLamed - 这是给avoid mixed-typed return values。我认为混合类型很危险,所以我写了这篇小文章。
    【解决方案5】:

    我真的不太确定一般来说哪种形式更可取。

    一方面,如果发生错误,您想要一个有用的消息,因此您最终会得到混合返回(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());
      }
    }
    

    但是,这似乎非常冗长,而且,我可以说,对我来说,Javaesque。

    【讨论】:

      【解决方案6】:

      关于您的问题,通常有两种类型的功能:

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

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

      不同的编程语言可能对这两种类型的命名有不同的约定。以C++ 为例,一种常见的命名是CHECK_XXX 用于类型2,IsXXX 用于类型1。 以下是取自 google-log 库的 tutorial 的示例:

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

      另一个例子是Vimscriptmaktaba 实用程序库,其中maktaba#value#IsXXX() 用于测试参数是否属于某种类型,而maktaba#ensure#IsXXX() 用于确保 @987654330 @ 持有并抛出异常。

      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。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        • 1970-01-01
        • 2011-09-21
        • 1970-01-01
        • 2013-08-23
        • 2023-04-08
        • 2013-07-21
        相关资源
        最近更新 更多