Assertions

It is a common practice in other programming languages to use assertions, which are also sometimes referred to as expectations, for documenting assumptions in the code. An assertion is a predicate that is expected to always be true at the point in the code where it is written:

if ($i % 2 == 0) {
    // ...
} else {
    assert($i % 2 == 1);

    // ...
}

In the code above, we use the expression assert($i % 2 == 1); to document our assumption that $i % 2 will always be 1 at this point in the code. This assertion serves as a precondition and describes the state the code that follows it expects to find before. Assertions can also be used as postconditions to describe the expected state after a unit of code finished executing. Preconditions and postconditions are also referred to as invariants.

Programming languages such as Eiffel that support design-by-contract evaluate preconditions and postconditions at compile-time. Just like C, C++, and Java, however, PHP interprets assertions at runtime. By default, PHP will raise a warning when the assertion fails:

Warning: assert(): assert($i % 2 == 2) failed

Another common practice is to use assert(false); to document areas of code that the developer does not expect to be reached:

switch ($i % 2) {
    case 0:
        // ...

    case 1:
        // ...

    default:
        assert(false);
}

Before PHP 7, assert() existed as a function that was inconvenient to use and had an adverse impact on performance. In PHP 7, assert() is a language construct that can be optimized away in production. And optionally, assert() can abort the execution of a program. The behavior of assert() is controlled by two configuration directives, zend.assertions and assert.exception.

When zend.assertions=1 (default) is set in php.ini, PHP’s compiler will generate bytecode for the assertion and PHP’s executor will execute the assertion. This is the suggested setting for development and testing environments. zend.assertions=1 can be set in the php.ini configuration file and using the ini_set() function at runtime.

When zend.assertions=0 is set then PHP’s compiler will generate bytecode for the assertion but PHP’s executor will not execute it. zend.assertions=0 can either be set in the php.ini configuration file, or by using the ini_set() function at runtime.

When zend.assertions=-1 is set then PHP’s compiler will not generate bytecode for the assertion, and it will not be executed. zend.assertions=-1 can only be set in the php.ini configuration file. Trying to set it at runtime using the ini_set() function results in a runtime error.

When assert.exception=0 (default) is set then a failed assertion results in a warning. This behavior is compatible with PHP 5. assert.exception=0 can be set in the php.ini configuration file, or by using the ini_set() function at runtime.

When assert.exception=1 is set then a Throwable will be raised for a failed assertion. An instance of AssertionError is used by default to represent the failed assertion. An object of a class that implements the Throwable interface, for instance a subclass of Exception, can be passed as the optional second parameter to assert(). This object will then be thrown instead of an AssertionError instance.

assert.exception=1 can be set in the php.ini configuration file and using the ini_set() function at runtime.

Please note that defining a function named assert() is now deprecated:

namespace vendor;

function assert()
{
    // ...
}

Executing the above sample would lead to the deprecation warning shown below:

PHP Deprecated: Defining a custom assert() function is deprecated,
as the function has special semantics in ...

Defining a function named assert() in the global namespace does not work anymore:

function assert()
{
    // ...
}

Executing the above sample would lead to the error shown below:

PHP Deprecated: Defining a custom assert() function is deprecated,
as the function has special semantics in ...
PHP Fatal error: Cannot redeclare assert() in ...

The changes made to assertions in PHP 7 are a welcome improvement over the clumsy assert() function of earlier PHP versions. If they could not be completely turned off by setting zend.assertions=-1 in PHP’s configuration file, these new assertions could be used to enrich PHP programming with fresh ideas. However, the vendor of a library, for instance, cannot rely on assertions being evaluated. Using assertions for design-by-contract style preconditions and postconditions therefore makes no sense if you have no control over the zend.assertions setting.

By the way: PHPUnit interprets an AssertionError raised by an assert() during the execution of a test as a failure instead of an error.