assert

Checks that a condition holds and raises an AssertionError when it does not.

Signature
assert(mixed $assertion, Throwable|string|null $description = null): bool
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$assertionmixedAny expression. Its result is evaluated for truthiness to decide whether the assertion passed.
$descriptionoptionalThrowable|string|null= nullIf a Throwable, that object is thrown when the assertion fails. If a string, it is used as the failure message. If omitted, PHP builds a description from the source code of the assertion at compile time.

Return value

boolAlways true when assertions are compiled out or disabled. Otherwise true if the assertion is truthy, and false if it is falsy and assertion failures are not configured to throw.

Errors

Throws AssertionError when the assertion fails and zend.assertions is 1, or throws the supplied Throwable when one is passed as the description.

Changelog

  • PHP 8.3.0All assert.* ini settings were deprecated. Use zend.assertions to control assertions instead.
  • PHP 8.0.0assert() no longer evaluates string arguments as PHP code; a string is treated like any other value and is simply checked for truthiness. If description is a Throwable it is thrown regardless of assert.exception, and no user callback is invoked.
  • PHP 7.2.0Passing a string as the assertion became deprecated.

assert() states something you believe is always true at that point in the code. It is not input validation — it is a claim about your own program's internal consistency, meant to catch the impossible during development.

Assertions are compiled away in production

The zend.assertions ini setting has three states, and it is the reason assert() is not just an if. Set to 1 (typical for development) assertions run and failures throw AssertionError. Set to 0 they are compiled in but skipped at runtime. Set to -1 — the recommended production value — the whole call is removed at compile time and costs literally nothing. That last value can only be set in php.ini; ini_set() cannot change it at runtime.

The consequence people get wrong

Because the call can vanish entirely, the assertion expression must have no side effects. assert($queue->pop() !== null) works in development and silently stops popping in production. Keep the expression a pure check.

Since PHP 8.0, a string assertion is no longer evaluated as code — it is just a truthy non-empty string, so assert('1 == 2') passes. Pass a real expression.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Document and enforce an invariant such as a sorted array or a non-empty internal cache during development.
  • Catch an impossible branch in a match or switch where a value should already have been validated.
  • Check a private helper's preconditions without paying any runtime cost on production servers.

Frequently asked questions

What is the difference between assert and throwing an exception?

An exception is part of your program's behaviour and always runs; an assertion is a development-only check that can be compiled out entirely with zend.assertions=-1. Use exceptions for anything that depends on user input or external state, and assertions only for internal invariants.

How do I disable assertions in production?

Set zend.assertions=-1 in php.ini, which removes assert() calls at compile time so they cost nothing. It cannot be changed with ini_set() at runtime — the value 0 is the runtime-changeable option, and it still compiles the calls in.

Why does assert('1 == 2') not fail in PHP 8?

Since PHP 8.0 assert() no longer evaluates string arguments as PHP code; the string is treated as an ordinary value and any non-empty string is truthy, so the assertion passes. Pass the expression itself, as assert(1 == 2).

More examples

Throwing a specific exception instead of AssertionError

Passing a Throwable as the second argument makes that object the thing that gets thrown, which lets a failing invariant flow into existing exception handling. Since PHP 8.0 this happens regardless of the old assert.exception setting. With zend.assertions off, nothing is thrown and the first message prints instead.

Example · php
<?php

class InvariantBroken extends RuntimeException {}

$cache = ['a' => 1, 'b' => 2];

try {
    assert(isset($cache['c']), new InvariantBroken('cache key c is missing'));
    echo "assertion passed (or assertions are disabled)\n";
} catch (InvariantBroken $e) {
    echo get_class($e), ': ', $e->getMessage(), "\n";
}

Why a string assertion no longer works

Before PHP 8 the string was eval'd as code and the first line would have failed. Now it is just a value, and any non-empty string passes — which silently disables every string-based assertion carried over from older code.

Example · php
<?php

var_dump(assert('1 == 2'));   // bool(true) - a non-empty string is truthy

try {
    assert(1 == 2, 'this one is a real expression');
    echo "not reached when zend.assertions=1\n";
} catch (AssertionError $e) {
    echo 'caught: ', $e->getMessage(), "\n";
}

Side effects disappear when assertions are compiled out

The same script produces different values depending on an ini setting, which is exactly the bug class to avoid. Whatever a production server has configured, code outside the assertion must never depend on work done inside one.

Example · php
<?php

$counter = 0;

function bump(int &$c): bool { $c++; return true; }

assert(bump($counter), 'do not do this');

echo "counter is {$counter}\n";
echo "With zend.assertions=-1 this prints 0; with 1 it prints 1.\n";

Discussion

  • Be the first to comment on this lesson.