我应该在我的PHP代码中使用assert吗?

时间:2010-12-23 06:33:37

标签: php assert

同事已经在我们的库中添加了assert命令几次,我将使用if语句并抛出异常。 (在此之前我从未听说过断言。)以下是他如何使用它的一个例子:

assert('isset($this->records); /* Records must be set before this is called. */');

我会做的:

if (!isset($this->records)) {
    throw new Exception('Records must be set before this is called');
}

从阅读the PHP docs on assert开始,看起来建议您确保assert处于活动状态并在使用assert之前添加处理程序。我找不到他这样做的地方。

所以,我的问题是,鉴于上述情况,使用断言是一个好主意,我应该更频繁地使用它而不是if和异常吗?

另一方面,我们计划在各种项目和服务器上使用这些库,包括我们甚至可能不参与的项目(库是开源的)。这在使用assert时有什么不同吗?

8 个答案:

答案 0 :(得分:77)

适用于大多数语言的经验法则(我隐约知道)是assert用于断言条件始终为真而{{1}如果可以想象它有时会失败,那么它是合适的。

在这种情况下,我会说if是合适的(基于我对情况的不了解),因为assert应该始终在给定方法之前设置调用。因此,未能设置记录将是程序中的错误而不是运行时条件。在这里,records有助于确保(经过充分测试)没有可能的程序执行路径,可能导致被assert保护的代码在没有assert的情况下被调用已经确定。

使用records而不是assert的优势在于,if通常可以在生产代码中关闭,从而减少开销。最好用assert处理的情况可能会在生产系统的运行期间发生,因此无法将其关闭。

答案 1 :(得分:24)

将断言视为“权力评论”。而不是像以下那样的评论:

// Note to developers: the parameter "a" should always be a number!!!

使用:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');

含义完全相同,适用于完全相同的受众,但第一条评论很容易被遗忘或忽略(无论感叹号多少),而“权力评论”不仅可供人类阅读并且理解,它在开发过程中也经常进行机器测试,如果你在代码和工作习惯中设置好的断言处理,就不会被忽略。

从这个角度来看,断言是一个完全不同于if(错误)......和异常的概念,它们可以共存。

是的,你应该评论你的代码,是的,你应该尽可能使用“权力评论”(断言)。

答案 2 :(得分:15)

完全取决于您的发展策略。大多数开发人员不了解assert()并使用下游单元测试。但主动和内置的测试方案有时是有利的。

断言很有用,因为它可以启用和禁用。如果没有定义这样的断言处理程序,它不会消耗性能。你的同事没有,你应该设计一些临时在开发环境中启用它的代码(如果E_NOTICE / E_WARNINGs打开,那么应该是断言处理程序)。偶尔使用它我的代码不能使用混合变量类型 - 我通常不会在弱类型的PHP中进行严格的输入,但是随机使用情况:

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));

例如,它可以弥补缺少类型说明符string $a, array $b。 PHP5.4将支持它们,但不支持。

答案 3 :(得分:7)

Assert不能替代正常的流量控制,如if或异常,因为它仅用于在开发过程中进行调试。

答案 4 :(得分:5)

关于早于7的PHP中断言的重要说明。与其他带有断言构造的语言不同,PHP并不完全抛出断言语句 - 它将其视为一个函数(在一个函数中执行debug_backtrace())通过断言)。关闭断言似乎只是将函数hotwire在引擎中无所事事。请注意,可以通过将zend.assertions设置为0而不是更正常的1(on)或-1(off)值来使PHP 7模拟此行为。

问题出现在assert将接受任何参数 - 但如果参数不是字符串,则assert获取表达式的结果,无论assert是on还是off。您可以使用以下代码块验证这一点。

<?php
  function foo($a) { 
    echo $a . "\n"; 
    return TRUE;
  }
  assert_options(ASSERT_ACTIVE, FALSE);

  assert( foo('You will see me.'));
  assert('foo(\'You will not see me.\')');

  assert_options(ASSERT_ACTIVE, TRUE);

  assert( foo('Now you will see'));
  assert('foo(\'both of us.\')');

鉴于断言的意图,这是一个错误,并且是一个长期存在的错误,因为自从在PHP 4中引入了assert以来它一直在使用该语言。

传递给assert的字符串是eval,其中包含所有性能影响和危险,但它是使断言语句按照PHP应用方式工作的唯一方法(这种行为在PHP中已弃用) 7.2)。

编辑:上面更改为注意PHP 7和7.2中的更改

答案 5 :(得分:4)

Assert只应在开发中使用,因为它对调试很有用。因此,如果您希望您可以使用它们来开发您的网站,但您应该使用例外的实时网站。

答案 6 :(得分:3)

您的同事真正尝试使用Eiffel语言并基于以下书籍来应用Design-by-Contract(TM):面向对象的软件构建,第2版。

他使用的断言将是Hoare Logic或Hoare Triple的{P} - 部分:{P} C {Q},其中{P}是前提断言断言(离子)s和{Q}是后置条件断言(离子)s。

我会对PHP中有关bug的断言功能提出批评意见。您不想使用错误代码。你真正想要的是PHP的制造商来修复断言中的错误。在他们这样做之前,你可以使用断言,但要注意它现在的错误状态。

此外,如果断言功能有问题,那么我建议您不要在生产代码中使用它。不过,我建议您在开发和测试代码中使用它。

最后 - 如果你对契约式设计进行研究,你会发现根据面向对象的经典继承使用布尔断言会产生后果 - 你必须永远不能削弱前提条件,也不能削弱后置条件。这样做可能会使您的多态后代对象彼此交互变得危险。直到你明白这意味着什么 - 我会不管它!

此外 - 我强烈建议PHP的制作者对“按合同设计”进行全面研究,并尝试尽快将其纳入PHP!然后,我们所有人都可以从拥有DbC感知的编译器/解释器中受益,该编译器/解释器将处理上述答案中提到的问题:

  1. 正确实现的Contract-by-Contract-aware编译器(希望)没有错误(与当前的PHP断言不同)。
  2. 一个正确实现的契约式设计编译器可以为您处理多态断言逻辑管理的细微差别,而不是在这个问题上绞尽脑汁!
  3. 注意:即使使用if语句作为断言(前置条件)的替代,如果用于强化前置条件或削弱后置条件,也将遭受可怕的后果。要了解这意味着什么,您需要学习设计合同才能知道! : - )

    快乐学习和学习。

答案 7 :(得分:3)

否,您的同事不应将其用作通用错误处理程序。根据手册:

  

断言仅应用作调试功能。您可以将它们用于完整性检查,以测试应该始终为TRUE的条件,如果条件不正确则指示某些编程错误,或者检查是否存在某些功能,例如扩展功能或某些系统限制和功能。

     

断言不应用于正常的运行时操作,例如输入参数检查。根据经验,如果未激活断言检查,您的代码应始终能够正常工作。

如果您熟悉自动化测试套件,则通常使用“ assert”动词来验证某些方法或函数的输出。例如:

function add($a, $b) {
    return $a + $b;
}

assert(add(2,2) == 5, 'Two and two is four, dummy!');
assert(is_numeric(add(2,2)), 'Output of this function to only return numeric values.');

您的同事不应将其用作通用错误处理程序,在这种情况下,不应将其用作输入检查。您的图书馆的某些用户似乎可能没有设置records字段。

相关问题