我应该在 PHP 代码中使用断言吗?
一位同事在我们的库中多次添加了 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');
}
阅读 关于断言的 PHP 文档,看起来建议您确保断言处于活动状态并在使用断言之前添加处理程序。我找不到他这样做的地方。
所以,我的问题是,考虑到上述情况,使用断言是一个好主意吗?我应该更频繁地使用它而不是 if 和异常吗?
另请注意,我们计划在各种项目和服务器上使用这些库,包括我们甚至可能不参与的项目(这些库是开源的)。这对使用断言有什么影响吗?
A coworker has added the assert command a few times within our libraries in places where I would have used an if statement and thrown an exception. (I had never even heard of assert before this.) Here is an example of how he used it:
assert('isset($this->records); /* Records must be set before this is called. */');
I would have done:
if (!isset($this->records)) {
throw new Exception('Records must be set before this is called');
}
From reading the PHP docs on assert, it looks like it's recommended that you make sure assert is active and add a handler before using assert. I can't find a place where he's done this.
So, my question is, is using assert a good idea given the above and should I be using it more often instead of if's and exceptions?
Another note, we are planning to use these libraries on a variety of projects and servers, including projects that we may not even be part of (the libraries are open source). Does this make any difference in using assert?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
适用于大多数语言(我隐约知道的所有语言)的经验法则是,断言用于断言条件始终为真,而
如果可以想象它有时会失败,则 if
是合适的。在这种情况下,我会说
assert
是合适的(基于我对情况的薄弱理解),因为records
应该始终设置在调用给定的方法。因此,未能设置记录将是程序中的错误,而不是运行时条件。在这里,assert
有助于确保(通过充分的测试)不存在可能导致调用assert
保护的代码的可能程序执行路径没有设置记录
。与
if
相比,使用assert
的优点是通常可以在生产代码中关闭assert
,从而减少开销。最适合使用if
处理的情况可能会发生在生产系统的运行时期间,因此无法关闭它们不会造成任何损失。The rule of thumb which is applicable across most languages (all that I vaguely know) is that an
assert
is used to assert that a condition is always true whereas anif
is appropriate if it is conceivable that it will sometimes fail.In this case, I would say that
assert
is appropriate (based on my weak understanding of the situation) becauserecords
should always be set before the given method is called. So a failure to set the record would be a bug in the program rather than a runtime condition. Here, theassert
is helping to ensure (with adequate testing) that there is no possible program execution path that could cause the code that is being guarded with theassert
to be called withoutrecords
having been set.The advantage of using
assert
as opposed toif
is thatassert
can generally be turned off in production code thus reducing overhead. The sort of situations that are best handled withif
could conceivably occur during runtime in production system and so nothing is lost by not being able to turn them off.将断言视为“强力注释”。而不是像这样的评论:
使用:
含义完全相同,面向完全相同的受众,但第一条评论很容易被遗忘或忽略(无论有多少感叹号),而“权力评论”不仅是它可供人类阅读和理解,并且在开发过程中不断经过机器测试,如果您在代码和工作习惯中设置良好的断言处理,它就不会被忽视。
这样看来,断言与 if(error)... 和异常是完全不同的概念,并且它们可以共存。
是的,您应该对代码进行注释,是的,您应该尽可能使用“强力注释”(断言)。
Think of asserts as "power comments". Rather than a comment like:
use:
The meanings are exactly the same and are intended for the exact same audience, but the first comment is easily forgotten or ignored (no matter how many exclamation marks), while the "power comment" is not only available for humans to read and understand, it is also constantly machine-tested during development, and won't be ignored if you set up good assert handling in code and in work habits.
Seen this way, asserts are a completely different concept than if(error)... and exceptions, and they can co-exist.
Yes, you should be commenting your code, and yes, you should be using "power comments" (asserts) whenever possible.
这完全取决于你的发展战略。大多数开发人员不知道
assert()
并使用下游单元测试。但主动和内置的测试方案有时可能是有利的。断言很有用,因为它可以启用和禁用。如果没有定义这样的断言处理程序,它不会消耗性能。您的同事没有,您应该设计一些代码,在开发环境中临时启用它(如果 E_NOTICE/E_WARNINGs 打开,那么断言处理程序也应该打开)。我偶尔会在我的代码无法承受混合变量类型的情况下使用它 - 我通常不会在弱类型 PHP 中进行严格类型输入,但有随机用例:
例如,这可以弥补类型说明符的缺乏
字符串$a,数组$b
。 PHP5.4将支持它们,但不检查。It wholly depends on your development strategy. Most developers are unaware of
assert()
and use downstream unit testing. But proactive and built-in testing schemes can sometimes be advantageous.assert is useful, because it can be enabled and disabled. It doesn't drain performance if no such assertion handler is defined. Your collegue doesn't have one, and you should devise some code which temporary enables it in the development environment (if E_NOTICE/E_WARNINGs are on, so should be the assertion handler). I use it occasionally where my code can't stomach mixed variable types - I don't normally engage in strict typing in a weakly typed PHP, but there random use cases:
Which for example would compensate for the lack of type specifiers
string $a, array $b
. PHP5.4 will support them, but not check.Assert 不能替代
if
等正常流程控制或异常,因为它仅用于开发期间的调试。Assert is not a substitute for normal flow control like
if
or exceptions, because it is only meant to be used for debugging during development.关于 PHP 7 之前版本中的断言的重要说明。与具有断言构造的其他语言不同,PHP 不会完全抛出断言语句 - 它将其视为一个函数(在断言调用的函数中执行 debug_backtrace())。关闭断言似乎只是将函数热线化为在引擎中不执行任何操作。请注意,可以通过将 zend.assertions 设置为 0 而不是更正常的值 1(打开)或 -1(关闭)来使 PHP 7 模拟此行为。
问题在于断言将接受任何参数 - 但如果参数不是字符串,则无论断言打开还是关闭,断言都会获取表达式的结果。您可以使用以下代码块验证这一点。
考虑到断言的意图,这是一个错误,而且自从 PHP 4 中引入断言以来,它就一直存在于该语言中,
这是一个长期存在的错误。传递给断言的字符串会被评估,以及随之而来的所有性能影响和危险,但这是让断言语句按照 PHP 中应有的方式工作的唯一方法(此行为在 PHP 7.2 中已弃用)。
编辑:上面进行了更改以记录 PHP 7 和 7.2 中的更改
An important note concerning assert in PHP earlier than 7. Unlike other languages with an assert construct, PHP doesn't throw assert statements out entirely - it treats it as a function (do a debug_backtrace() in a function called by an assertion). Turning asserts off seems to just hotwire the function into doing nothing in the engine. Note that PHP 7 can be made to emulate this behavior by setting zend.assertions to 0 instead of the more normal values of 1 (on) or -1 (off).
The problem arises in that assert will take any argument - but if the argument is not a string then assert gets the results of the expression whether assert is on or off. You can verify this with the following code block.
Given the intent of assert this is a bug, and a long standing one since it's been in the language since assert was introduced back in PHP 4.
Strings passed to assert are eval'ed, with all the performance implications and hazards that come with that, but it is the only way to get assert statements to work the way they should in PHP (This behavior deprecated in PHP 7.2).
EDIT: Changed above to note changes in PHP 7 and 7.2
您的同事实际上正在尝试应用 Eiffel 语言中的契约设计 (DbC)书:面向对象的软件构造,第二版。
正如他所使用的,该断言将是霍尔逻辑或霍尔三元组的 {P} 部分:{P} C {Q},其中 {P} 是前置条件断言(ion),{Q} 是后置条件断言(ion)。
我会认真对待有关 PHP 中断言功能存在错误的建议。您不想使用有错误的代码。你真正想要的是 PHP 的开发者修复断言中的错误。在他们这样做之前,您可以使用断言,但使用它时要注意其当前的错误状态。
此外,如果断言功能有问题,那么我建议您不要在生产代码中使用它。尽管如此,我还是建议您在适当的情况下在开发和测试代码中使用它。
最后,如果您研究契约设计,您会发现根据面向对象的经典继承使用布尔断言会产生后果,也就是说,您绝不能削弱前置条件,也不能削弱后置条件。这样做可能会对相互交互的多态后代对象造成危险。在你明白这意味着什么之前——我不会管它!
而且——我强烈建议PHP的开发者对契约设计进行全面的研究,并尝试尽快将其应用到PHP中!那么我们所有人都可以从拥有 DbC 感知编译器/解释器中受益,它将处理答案中指出的问题(上面):
注意:即使使用
if
语句作为断言(前置条件)的替代品,如果用于加强前置条件或削弱后置条件,也会遭受可怕的后果。要理解这意味着什么,您需要研究合同设计才能知道! :-)快乐学习。
You coworker is really attempting to apply design by contract (DbC) from the Eiffel language and based on the book: Object Oriented Software Construction, 2nd Edition.
The assertion, as he used it, would be the {P}-part of the Hoare Logic or Hoare Triple: {P} C {Q}, where the {P} is the precondition assert(ion)s and {Q} are the post-condition assert(ion)s.
I would take critical note of advice given about the assert feature in PHP having bugs. You don't want to use buggy code. What you really want are the makers of PHP to fix the bug in the assert. Until they do, you can use the assert, but use it mindful of its present buggy state.
Moreover, if the assert feature is buggy, then I suggest you do not use it in production code. Nevertheless, I do recommend that you use it in development and testing code where appropriate.
Finally—if you do a study of design by contract, you will find that there are consequences to using Boolean assertions in light of object-oriented classical inheritance—that is—you must must never weaken a precondition, nor weaken a post-condition. Doing so could be dangerous to your polymorphic descendant objects interacting with each other. Until you understand what that means—I'd leave it alone!
Moreover—I highly recommend that the makers of PHP do a comprehensive study of design by contract and attempt to put it into PHP ASAP! Then all of us can benefit from having a DbC-aware compiler/interpreter, which would handle the issues noted in the answers (above):
NOTE: Even your use of an
if
-statement as a substitute for the assert (precondition) will suffer dire consequences if used to strengthen a precondition or weaken a post-condition. To understand what that means, you will need to study design by contract to know! :-)Happy studying and learning.
断言只能在开发中使用,因为它对于调试很有用。因此,如果您愿意,您可以使用它们来开发您的网站,但您应该对实时网站使用例外。
Assert should only be used in development as it is useful for debugging. So if you want you can use them for developing your website, but you should use exceptions for a live website.
不,您的同事不应该将其用作通用错误处理程序。根据手册:
如果您熟悉自动化测试套件,那么“assert”动词通常用于验证某些方法或函数的输出。例如:
您的同事不应将其用作通用错误处理程序,在本例中不应将其用作输入检查。看起来您的图书馆的某些用户可能没有设置记录字段。
No, your co-worker shouldn't be using it as a general purpose error handler. According to the manual:
If you are familiar with automated test suites, the "assert" verb is generally used to verify the output of some method or function. For example:
Your co-worker shouldn't be using it as a general purpose error handler and in this case as an input check. It looks like it's possible for the records field to not be set by some user of your library.