100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
PHP

Type Declarations and Strict Types

PHP supports optional scalar and object type declarations on parameters, return values, and properties, with strict_types controlling how aggressively the engine coerces mismatched values.

Modern PHP FeaturesIntermediate9 min readJul 9, 2026
Analogies

Type Declarations and Strict Types

PHP started as a loosely typed language, but since PHP 7 it has steadily grown a real type system layered on top: scalar type declarations (int, float, string, bool), compound types (array, iterable, callable, object), class/interface types, self/static, void/never for return position, nullable types via a leading ?, and union/intersection types in modern versions. These declarations can appear on function/method parameters, return types, and — since PHP 7.4 — typed class properties. Type declarations are enforced at runtime: if a value doesn't match (and can't be coerced), PHP throws a TypeError, catching a class of bugs that would otherwise surface much later.

🏏

Cricket analogy: A team sheet enforces that the 'captain' slot must be filled by a player object, not a placeholder, allows a 'vice-captain' slot to be nullable if none is named, and rejects an ineligible entry outright at selection time the way a TypeError halts a mismatched call.

Coercive mode versus strict_types

By default PHP runs in 'coercive' (weak) typing mode: passing the string "5" to an int parameter is silently converted to the integer 5. Adding declare(strict_types=1); as the very first statement in a file switches that file to strict mode for calls made from within it: scalar type mismatches — passing a string where int is declared — now throw a TypeError instead of coercing, with the sole exception that an int is still accepted for a float parameter (a widening conversion PHP always allows). Crucially, strict_types is a per-file declaration that governs the calling file, not the receiving function's file — a strict-mode caller enforces strict checks even when calling a function defined in a non-strict file, and vice versa.

🏏

Cricket analogy: A relaxed net session lets a bowler practice off any run-up length without correction, but a strict academy session enforces exact run-up measurements from the first drill, though it still accepts a slightly longer, more precise run-up as an upgrade, and this strictness is set per-academy, declared in the very first session rule.

php
<?php
declare(strict_types=1);

function applyDiscount(float $price, int $percentOff): float
{
    return $price * (1 - $percentOff / 100);
}

echo applyDiscount(100.0, 20);   // 80.0 - fine, types match exactly
echo applyDiscount(100, 20);     // 80.0 - int widens to float automatically

try {
    echo applyDiscount("100", 20); // TypeError under strict_types: string is not float
} catch (\TypeError $e) {
    echo 'Caught: ' . $e->getMessage();
}

Nullable, union, and intersection types

A ?Type prefix (e.g. ?string) means the parameter accepts either that type or null, shorthand for the union Type|null. PHP 8.0 introduced full union types, letting a signature declare int|string $id, matching any value of either listed type. PHP 8.1 added intersection types (Countable&Iterator), requiring a value to satisfy every listed type simultaneously — useful for generic collection parameters that must both be iterable and countable. PHP 8.0 also introduced the mixed type, meaning literally any value is accepted, and never for functions that always throw or otherwise never return control to the caller.

🏏

Cricket analogy: A 'next batter' slot might be nullable, shorthand for 'player or none if all out,' a 'man of the match' field might accept a batter-or-bowler union, and a 'genuine all-rounder' role requires satisfying both batting and bowling criteria simultaneously, an intersection, while a pundit's comment field accepts literally anything, and a 'retired hurt, never returning' status never resumes play.

php
<?php
declare(strict_types=1);

function findUser(int|string $identifier): ?array
{
    // returns null if not found, or an array if found
    return match (true) {
        is_int($identifier) => ['id' => $identifier, 'name' => 'Ada'],
        default => null,
    };
}

class ValidationException extends \RuntimeException {}

function assertPositive(int $value): void
{
    if ($value <= 0) {
        throw new ValidationException('Value must be positive');
    }
}

A useful analogy: coercive typing is like a lenient border guard who'll let you through if your documents are 'close enough' and quietly stamps them into the right format. strict_types is a strict guard who rejects anything that isn't exactly the declared type — except the one deliberate exemption of letting an int pass as a float, since no precision is lost widening an integer into a floating-point value.

A common misconception is that declare(strict_types=1) affects the whole application once set anywhere. It does not — it only governs type-checking for calls made *from* that specific file. Mixing strict and non-strict files in the same codebase is legal but can produce confusing inconsistencies, so most modern PHP style guides recommend declaring strict_types=1 in every file.

  • Type declarations can appear on parameters, return types, and typed properties, and are enforced at runtime with TypeError on mismatch.
  • Coercive (default) mode silently converts compatible scalar values; declare(strict_types=1) disables most of that coercion.
  • strict_types is a per-file setting that governs the calling file's outgoing calls, not the definition file.
  • Even under strict_types, an int argument is still accepted for a float parameter as a safe widening conversion.
  • Union types (int|string) accept any of the listed types; intersection types (Countable&Iterator) require all of them simultaneously.
  • mixed accepts any value and never marks functions that never return control normally (always throw or exit).

Practice what you learned

Was this page helpful?

Topics covered

#PHP#PHPProgrammingStudyNotes#Programming#TypeDeclarationsAndStrictTypes#Type#Declarations#Strict#Types#StudyNotes#SkillVeris