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

Dar*_*ein 84 php assert

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

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

我会做的:

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

通过在assert上阅读PHP文档,看起来建议您确保assert处于活动状态并在使用assert之前添加处理程序.我找不到他这样做的地方.

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

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

aar*_*ing 77

适用于大多数语言的经验法则(我隐约知道)是a assert用于断言条件总是正确的,而if如果可以想象它有时会失败则适用.

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

与使用assert相反的优点ifassert通常可以在生产代码中关闭,从而减少开销.最好处理的那种情况if可能会在生产系统的运行时期间发生,因此不会因为无法将其关闭而丢失.

  • 晚会,但PHP.net声明:"断言应仅用作调试功能." (9认同)
  • 要添加到此,您可能不希望在生产代码中禁用断言,因为它们有助于确保"这应该永远不会发生"的条件保持这种状态.让应用程序从断言中停止而不是让您的用户继续沿着不应存在的执行路径运行可能会更好. (4认同)
  • @derekerdmann我不同意这一点(在php中使用assert()的上下文中).这是一个很大的漏洞缺口,因为assert()将所有字符串参数视为PHP代码,因此(理论上)可以注入和运行任意代码.恕我直言,断言应该关闭生产 (4认同)
  • @derekerdmann:是的。对于某些断言,将它们记录(在生产中)或打印警告(在开发环境中)可能就足够了。但是由于通常断言也可以保护与安全相关的代码,因此您最好启用`assert_options(ASSERT_BAIL)`。无论如何,它比手动的/抛出解决方法要快。 (2认同)
  • @VitaliyLebedev如果你不想注意不要传递字符串断言. (2认同)

Dav*_*ley 25

将断言视为"权力评论".而不是像这样的评论:

// Note to developers: the parameter "a" should always be a number!!!
Run Code Online (Sandbox Code Playgroud)

使用:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 如果在开发时测试时您总是将良好的条件传递给断言,但在生产中如果关闭断言 - 某些用户传递了您在测试时没有考虑到的另一个条件,该怎么办?或者,您需要始终保持断言,但这与编写自己的支票不一样吗? (2认同)

mar*_*rio 16

这完全取决于您的发展战略.大多数开发人员都不知道assert()并使用下游单元测试.但主动和内置测试方案有时可能是有利的.

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

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));
Run Code Online (Sandbox Code Playgroud)

例如,这将弥补类型说明符的不足string $a, array $b.PHP5.4将支持它们,但不检查.


Mar*_*ich 7

断言不能代替正常的流控制if或异常,因为它仅用于在开发期间进行调试.


Mic*_*ris 5

关于PHP中早于7的assert的重要说明。与其他具有assert构造的语言不同,PHP不会完全抛出assert语句-它将其视为函数(在由断言调用的函数中执行debug_backtrace())。关闭断言似乎只是使函数热衷于在引擎中什么也不做。请注意,可以通过将zend.assertions设置为0而不是更普通的值1(on)或-1(off)来使PHP 7模仿此行为。

问题是断言将接受任何参数-但是,如果参数不是字符串,则断言无论断言是打开还是关闭都将获取表达式的结果。您可以使用以下代码块进行验证。

<?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.\')');
Run Code Online (Sandbox Code Playgroud)

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

评估传递给assert的字符串,以及随之而来的所有性能影响和危害,但这是使assert语句按PHP方式工作的唯一方法(此行为在PHP 7.2中已弃用)。

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


Lar*_*rry 5

您的同事实际上正在尝试应用Eiffel 语言中的契约设计(DbC),并基于《面向对象软件构建》第二版一书。

\n\n

正如他所使用的,该断言将是霍尔逻辑或霍尔三元组的 {P} 部分:{P} C {Q},其中 {P} 是前置条件断言(ion),{Q} 是后置条件断言(ion)。

\n\n

我会认真对待有关 PHP 中断言功能存在错误的建议。您不想使用有错误的代码。你真正想要的是 PHP 的开发者修复断言中的错误。在他们这样做之前,您可以使用断言,但使用它时要注意其当前的错误状态。

\n\n

此外,如果断言功能有错误,那么我建议您不要在生产代码中使用它。尽管如此,我还是建议您在适当的情况下在开发和测试代码中使用它。

\n\n

最后\xe2\x80\x94如果你研究契约设计,你会发现根据面向对象的经典继承使用布尔断言会产生后果\xe2\x80\x94也就是说\xe2\x80\x94你必须永远不要削弱前提条件,也不要削弱后置条件。这样做可能会对彼此交互的多态后代对象造成危险。在你明白这意味着什么之前\xe2\x80\x94\我不会管它!

\n\n

另外\xe2\x80\x94我强烈建议PHP的开发者对契约设计进行全面的研究,并尝试尽快将其移植到PHP中!那么我们所有人都可以从拥有 DbC 感知的编译器/解释器中受益,它将处理答案中指出的问题(上面):

\n\n
    \n
  1. 正确实现的合同设计编译器(希望)不会出现错误(与当前的 PHP 断言不同)。
  2. \n
  3. 正确实现的契约设计编译器将为您处理多态断言逻辑管理的细微差别,而不是为这个问题绞尽脑汁!
  4. \n
\n\n

注意:即使您使用if语句作为断言(前置条件)的替代品,如果用于加强前置条件或削弱后置条件,也会遭受可怕的后果。要理解这意味着什么,您需要研究合同设计才能知道!:-)

\n\n

快乐的学习和学习。

\n