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.