91

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?

thanksd
  • 54,176
  • 22
  • 157
  • 150
Darryl Hein
  • 142,451
  • 95
  • 218
  • 261

8 Answers8

84

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 an if 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) because records 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, the assert 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 the assert to be called without records having been set.

The advantage of using assert as opposed to if is that assert can generally be turned off in production code thus reducing overhead. The sort of situations that are best handled with if could conceivably occur during runtime in production system and so nothing is lost by not being able to turn them off.

aaronasterling
  • 68,820
  • 20
  • 127
  • 125
  • 6
    To add to this, you may not want to disable assertions in your production code, because they help make sure that those "this should never happen" conditions stay that way. It may be better to let your application stop from an assert than to let your users keep going along an execution path that should not exist. – derekerdmann Dec 23 '10 at 07:00
  • 3
    @derekerdmann: True. For some assertions it might be sufficient to log them (in production) or print the warning (in development environment). But since oftentimes asserts also protect security relevant code, you might as well enable `assert_options(ASSERT_BAIL)`. It's faster than manual if/throw workarounds anyway. – mario Dec 23 '10 at 07:07
  • 4
    @derekerdmann I would disagree on this (in context of using assert() in php). This is a big vulnerability gap, as assert() treats all string arguments as PHP code, thus it is (theoretically) possible to inject and run arbitrary code. IMHO, assertions should be turned off on production – Vitalii Lebediev Mar 27 '13 at 10:45
  • 2
    @VitaliyLebedev if you don't want to be susceptible to injection don't pass strings to assert. – Damon Snyder Sep 05 '14 at 16:07
  • 10
    Late to the party but PHP.net states: "Assertions should be used as a debugging feature only." – Koen. Dec 13 '14 at 23:06
  • 1
    When it comes to throw a `LogicException`, I try to use `assert` instead of `if`. – luc.chante Jul 04 '17 at 12:01
  • 1
    @Vitaliy "This is a big vulnerability gap, as assert() treats all string arguments as PHP code" As of PHP 7.2, string assertions are deprecated; the assertion should be an expression instead: https://secure.php.net/manual/en/function.assert.php#refsect1-function.assert-parameters – kelvin Nov 20 '18 at 19:50
  • What I feel is not often said is that `assert` in PHP is a **language construct** (in PHP 7) which - simplifying - means that any code inside `assert` will NOT be executed if assertions are turned off. – SteveB Aug 05 '20 at 08:22
30

Think of asserts as "power comments". Rather than a comment like:

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

use:

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

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.

FelixSFD
  • 6,052
  • 10
  • 43
  • 117
DaveWalley
  • 817
  • 10
  • 22
  • 2
    what if on development when testing you always pass good condition to the assert but in production if assert is turned off - some user passes another condition which you did not think about when testing? Or otherwise you need to keep assert always on, but then is it not the same as writing your own check? – Dariux Jul 26 '14 at 09:51
  • 1
    Then your program will fail. Fix it properly with if statements and the error handling features of your language and development environment. asserts can uncover problems, there are better ways of fixing problems. – DaveWalley Jul 27 '14 at 21:11
  • Note that as of PHP 7.2 passing a string into assert for evaluation has been deprecated. Sad, because it looked quite handy. – Jannie Theunissen May 11 '20 at 06:53
  • Using `assert()` can also be *very* useful when working with static analysis tools like Psalm. If a return type only specifies an instance of a given interface, say, but you call methods defined on the actual returned concrete class that aren't defined on the interface (Zend 1 is terrible for doing this), Psalm will flag it as an issue. By using an assert to verify the actual concrete class, you can tell Psalm that it's actually an instance of that class and so will implement that method. – Matthew Daly Apr 05 '22 at 11:51
16

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:

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

Which for example would compensate for the lack of type specifiers string $a, array $b. PHP5.4 will support them, but not check.

mario
  • 144,265
  • 20
  • 237
  • 291
9

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.

Mark Snidovich
  • 1,055
  • 7
  • 11
6

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.

<?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.\')');

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

Michael Morris
  • 531
  • 5
  • 7
  • 1
    In PHP 7 there is/will be [`zend.assertions`](https://wiki.php.net/rfc/expectations) ini setting to completely turn off `assert()`. – Kontrollfreak Jul 11 '15 at 23:27
  • That is excellent news - but based on the documentation there it looks like a patch to PHPUnit is order, adding an assert callback handler to throw AssertionException when assertions fail under PHP 5.x. This way Unit tests can use the annotation @expectedException AssertionException regardless of whether they run on PHP 5.x or 7. – Michael Morris Jul 14 '15 at 15:26
5

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):

  1. A properly implemented design-by-contract-aware compiler would (hopefully) be bug-free (unlike the current PHP assert).
  2. A properly implemented design-by-contract-aware compiler would handle the nuances of polymorphic assertion logic management for you instead of racking your brain over the matter!

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Liberty Lover
  • 844
  • 10
  • 12
3

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.

Kyle
  • 1,757
  • 1
  • 12
  • 22
3

No, your co-worker shouldn't be using it as a general purpose error handler. According to the manual:

Assertions should be used as a debugging feature only. You may use them for sanity-checks that test for conditions that should always be TRUE and that indicate some programming errors if not or to check for the presence of certain features like extension functions or certain system limits and features.

Assertions should not be used for normal runtime operations like input parameter checks. As a rule of thumb your code should always be able to work correctly if assertion checking is not activated.

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:

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.');

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.

Community
  • 1
  • 1
Dean Or
  • 2,822
  • 2
  • 26
  • 25