Strict types and the type system

PHP · Practical · 14 min

Types catch bugs at the call site and make static analysis and IDE support work.

By default PHP runs in coercive mode: a scalar argument of the wrong type is converted if possible, so passing "5" to an int parameter gives 5. Put this as the very first statement of a file to switch to strict mode:

declare(strict_types=1);

Now only an exact scalar type is accepted (the one exception is an int passed to a float). The setting applies to calls made from that file, not to the functions defined in it, so every file opts in for itself. Most modern codebases put it in every file.

Types you can declare on parameters, returns and properties:

  • Scalars int, float, string, bool, plus array, callable, iterable, object, class and interface names.
  • Nullable ?User and union int|string (PHP 8.0); intersection Countable&Iterator (8.1); combinations like (A&B)|null (8.2).
  • mixed (anything), void (returns nothing), never (always throws or exits), static (the called class), and null, false and true as standalone types (8.2).

A mismatch throws a TypeError. Typed properties are uninitialized until assigned, and reading one before that throws an Error.

Implicitly nullable parameters, function f(User $u = null), are deprecated in PHP 8.4; write ?User $u = null.

Example

<?php
declare(strict_types=1);

function area(float $w, float $h): float {
    return $w * $h;
}

function findId(int|string $key): ?int {
    return is_numeric($key) ? (int) $key : null;
}

function fail(string $msg): never {
    throw new RuntimeException($msg);
}

echo area(2, 3.5), "\n";        // 7 (int to float is allowed)
var_dump(findId('x'));          // NULL

try {
    area("2", 3);               // string not accepted in strict mode
} catch (TypeError $e) {
    echo "TypeError\n";         // TypeError
}

Remember

  • declare(strict_types=1) affects calls made from that file
  • Use unions, nullables and never to describe real contracts
  • Type mismatches throw TypeError instead of silently converting

Watch out for

  • Expecting strict_types in a library file to protect its functions from callers in non-strict files.

Check yourself

  1. File A has strict_types=1 and defines f(int $x). File B, without strict_types, calls f("5"). What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.php.net/manual/en/language.types.declarations.php