Skip to main content

Assertions

The Assert class provides runtime assertion utilities for verifying preconditions, postconditions, and invariants in your code. Unlike validation, assertions verify assumptions that must be true for the program to continue safely.

Purpose​

Runtime verification of programming assumptions with static analysis integration. When assertions fail, they indicate bugs that should be fixed rather than conditions that should be handled gracefully.

Key Methods​

Boolean Assertions​

  • true(mixed $value) - Asserts that a value is boolean true
  • false(mixed $value) - Asserts that a value is boolean false

Null Assertions​

  • notNull(mixed $value) - Asserts that a value is not null

Type Assertions​

  • string(mixed $value) - Asserts that a value is a string

Usage Scenarios​

Precondition Verification​

Verify function inputs meet required assumptions:

public function processUser(?User $user): void
{
Assert::notNull($user);
// $user is guaranteed non-null here
$user->doSomething();
}

Type Narrowing with Static Analysis​

Use assertions to help static analysis tools understand type guarantees:

function handleValue(mixed $value): void
{
Assert::string($value);
// PHPStan now knows $value is string
echo strtoupper($value);
}

Postcondition Verification​

Verify function outputs meet expected conditions:

public function findUser(int $id): User
{
$user = $this->repository->find($id);
Assert::notNull($user);
return $user;
}

Invariant Checking​

Verify state consistency during complex operations:

public function transfer(Account $from, Account $to, int $amount): void
{
$totalBefore = $from->balance + $to->balance;

$from->withdraw($amount);
$to->deposit($amount);

$totalAfter = $from->balance + $to->balance;
Assert::true($totalBefore === $totalAfter);
}

When to Use Assertions​

Use Assertions For:​

  • Programming errors - Violations of assumed contracts or invariants
  • Developer mistakes - Incorrect API usage or invalid state
  • Type narrowing - Helping static analysis understand guarantees
  • Defensive programming - Fail fast when assumptions are violated
  • Development debugging - Catching bugs early in development

Don't Use Assertions For:​

  • User input validation - Use validation classes instead
  • Expected error conditions - Use proper error handling
  • Business logic validation - Use domain-specific validation
  • Recoverable errors - Use exceptions meant to be caught
  • Production-only checks - Assertions should be permanent

Exception Handling​

Should You Catch Assertion Exceptions?​

Generally, no. Assertion failures indicate programming errors that should halt execution:

// DON'T catch assertion failures
try {
Assert::notNull($user);
$user->doSomething();
} catch (AssertionFailedException $e) {
// This masks a bug that should be fixed
}

When an assertion fails, the program should crash with a clear error message indicating what assumption was violated. This helps developers identify and fix bugs quickly.

Exception Details​

  • Throws: AssertionFailedException
  • Exit code: ExitCode::GENERAL_ERROR
  • Message: Descriptive text indicating which assertion failed

When Catching Is Acceptable​

Only catch assertion exceptions in specific scenarios:

  • Testing - Verify that assertions work correctly
  • Framework-level error handling - Global exception handlers that log and report
  • Development tools - IDE integrations or debugging utilities
// Acceptable in tests
test('throws when value is null', function () {
expect(fn() => Assert::notNull(null))
->toThrow(AssertionFailedException::class);
});

Static Analysis Integration​

All assertion methods include PHPStan annotations for type narrowing:

  • @phpstan-assert true $value - Narrows to boolean true
  • @phpstan-assert false $value - Narrows to boolean false
  • @phpstan-assert !null $value - Narrows to non-null
  • @phpstan-assert string $value - Narrows to string type

This enables static analysis tools to understand the guarantees provided by assertions:

function example(mixed $value): void
{
Assert::string($value);
// No PHPStan error: $value is known to be string
echo strtoupper($value);
}

Assertions vs Validation​

Assertions​

  • Verify programming assumptions
  • Should never fail in correct code
  • Indicate bugs when they fail
  • Not meant to be caught
  • Used for development and debugging

Validation​

  • Verify user input or external data
  • Expected to fail sometimes
  • Indicate invalid input when they fail
  • Meant to be caught and handled
  • Used for business logic and security

Best Practices​

Write Clear Assertion Messages​

Boolean assertions check the type first and provide specific error messages:

Assert::true($value);
// "Failed to assert true: value is not a boolean."
// or "Failed to assert true."

Fail Fast​

Place assertions early in functions to catch violations immediately:

public function process(mixed $data): void
{
Assert::string($data); // Fail fast
// Rest of function knows $data is string
}

Use Appropriate Assertion Types​

Choose the most specific assertion for your needs:

// Good: Specific assertion
Assert::string($value);

// Bad: Too generic
Assert::true(is_string($value));

Don't Assert Expected Conditions​

Only assert conditions that indicate bugs:

// Bad: User input validation should not use assertions
Assert::true($email !== '');

// Good: Use proper validation
if ($email === '') {
throw new ValidationException('Email is required');
}

Performance Considerations​

  • Assertions execute on every call in all environments
  • No performance overhead for assertion checking
  • Failed assertions halt execution immediately
  • Consider the cost of complex assertion conditions

Common Patterns​

Guard Clauses with Assertions​

public function updateUser(?User $user): void
{
Assert::notNull($user);
// Guaranteed non-null, continue safely
$user->update(['last_seen' => now()]);
}

Type-Safe Conversions​

public function formatName(mixed $name): string
{
Assert::string($name);
return Str::title($name);
}

Invariant Maintenance​

public function setBalance(int $balance): void
{
$this->balance = $balance;
Assert::true($this->balance >= 0);
}
  • AssertionFailedException - Exception thrown on assertion failures
  • ExitCode - Provides exit code constants including GENERAL_ERROR
  • PHPStan - Static analysis tool that understands assertion annotations