Type Declarations in Functions

Yo Yogesh.M Updated 17 Oct 2026
8 min read ·Lesson 52 of 102
In this post

What Are Type Declarations? In simple terms: Type declarations state what kind of value a function expects for each parameter and what kind of value it returns — for example, "this parameter must be a

What Are Type Declarations?

In simple terms: Type declarations state what kind of value a function expects for each parameter and what kind of value it returns — for example, "this parameter must be an integer" or "this function returns a string".

Technically: A type declaration is written before a parameter name (int $quantity) or after the parameter list (): string). PHP checks the value at runtime when the function is called and when it returns. If a value does not match (and cannot be converted, in coercive mode), PHP throws a TypeError.

function lineTotal(int $quantity, float $unitPrice): float
{
    return $quantity * $unitPrice;
}

Why Type Declarations Matter

BenefitExplanation
Errors appear earlyA wrong value fails immediately at the function boundary, not later in confusing ways.
Self-documenting codeThe signature shows exactly what a function needs and produces.
Better toolsEditors autocomplete and warn; static analyzers (PHPStan, Psalm) detect bugs before code runs.
Safer refactoringChanging a function's contract makes incompatible calls fail visibly.

Without types, lineTotal("3 pcs", null) would silently produce a wrong number. With types, PHP reports the problem.


Available Types

TypeAcceptsExample
intIntegersint $quantity
floatFloats (and integers, which are converted)float $price
stringStringsstring $name
booltrue / falsebool $isActive
arrayArraysarray $items
callableFunctions, closures, callable strings/arrayscallable $callback
iterableArrays and Traversable objects (generators, iterators)iterable $rows
objectAny objectobject $entity
Class / interface nameObjects of that class, a subclass, or an implementing classDateTimeInterface $date
self, static, parentThe current class (and related) — used in classes): static
mixedAny value, including null (PHP 8.0+)mixed $value
voidReturn type only: returns nothing): void
neverReturn type only: never returns (PHP 8.1+)): never
null, false, trueExactly that value; mostly used in unions (standalone since PHP 8.2)): int|false

Composite Types

FormSyntaxMeaningSince
Nullable?stringstring or nullPHP 7.1
Unionint|stringEither typePHP 8.0
IntersectionCountable&TraversableMust satisfy all types (classes/interfaces only)PHP 8.1
DNF (disjunctive normal form)(Countable&Traversable)|nullUnions of intersectionsPHP 8.2

Scalar Types

Example

<?php
function area(float $width, float $height): float
{
    return $width * $height;
}

var_dump(area(4, 2.5));
var_dump(area('3', '2'));

try {
    area('wide', 3);
} catch (TypeError $e) {
    echo $e->getMessage() . "\n";
}

Output

float(10)
float(6)
area(): Argument #1 ($width) must be of type float, string given, called in /path/script.php on line 11

How It Works

  1. area(4, 2.5): the integer 4 is accepted for a float parameter and converted to 4.0.
  2. area('3', '2'): in the default coercive mode, numeric strings are converted to floats.
  3. area('wide', 3): 'wide' cannot be converted, so PHP throws a TypeError. The message names the function, the argument position, the parameter, the expected and actual types, and the line of the call.

Coercive Mode vs Strict Mode

By default, PHP converts compatible scalar values (coercive typing). Adding declare(strict_types=1); as the first statement of a file makes calls from that file strict:

<?php
declare(strict_types=1);

function area(float $width, float $height): float
{
    return $width * $height;
}

var_dump(area(4, 2.5));

try {
    area('3', '2');
} catch (TypeError $e) {
    echo "TypeError: numeric strings are rejected in strict mode\n";
}

Output

float(10)
TypeError: numeric strings are rejected in strict mode
ArgumentCoercive Mode (default)Strict Mode
int → float parameterAccepted (converted)Accepted (the only allowed conversion)
"3" → int/float parameterAccepted (converted)TypeError
3.0 → int parameterAccepted (converted to 3)TypeError
3.5 → int parameterConverted to 3 with a deprecation notice (PHP 8.1+)TypeError
"abc" → int parameterTypeErrorTypeError

Strict mode is covered in detail in Strict Types. Many modern projects enable it in every file.


Nullable Types

A ? before a type allows null as well:

<?php
function greet(?string $name): string
{
    return 'Hello, ' . ($name ?? 'guest');
}

echo greet('Meena') . "\n";
echo greet(null) . "\n";

Output

Hello, Meena
Hello, guest

?string is equivalent to the union string|null. Note that a nullable parameter is not automatically optional: greet() without an argument still fails. To make it optional, add a default: ?string $name = null.


Union Types (PHP 8.0+)

A union type accepts values of any of the listed types:

<?php
function formatId(int|string $id): string
{
    return is_int($id) ? sprintf('#%06d', $id) : strtoupper($id);
}

echo formatId(42) . "\n";
echo formatId('ord-77') . "\n";

Output

#000042
ORD-77

Inside the function, is_int() determines which kind of value was received. Union types are useful when a value legitimately comes in different forms; if a parameter accepts many unrelated types, the function is probably doing too much.

false in Unions

Functions that return a value or false on failure can say so precisely:

<?php
function firstWordLength(string $text): int|false
{
    $space = strpos($text, ' ');

    return $space === false ? false : $space;
}

var_dump(firstWordLength('Hello world'));
var_dump(firstWordLength('Hello'));

Output

int(5)
bool(false)

Class and Interface Types

A class or interface name as a type accepts objects of that class, its subclasses, or classes implementing that interface:

<?php
function formatDate(DateTimeInterface $date): string
{
    return $date->format('d M Y');
}

echo formatDate(new DateTimeImmutable('2026-10-09')) . "\n";
echo formatDate(new DateTime('2026-12-25')) . "\n";

Output

09 Oct 2026
25 Dec 2026

Both DateTime and DateTimeImmutable implement DateTimeInterface, so the function accepts either. Typing against an interface rather than a specific class makes functions more flexible. This is a key idea in object-oriented design (see PHP Interfaces).

Intersection Types (PHP 8.1+)

An intersection type requires an object to implement all listed types:

<?php
function describeCollection(Countable&Traversable $items): string
{
    $values = [];

    foreach ($items as $item) {
        $values[] = $item;
    }

    return count($items) . ' items: ' . implode(', ', $values);
}

echo describeCollection(new ArrayObject(['PHP', 'MySQL'])) . "\n";

Output

2 items: PHP, MySQL

The function uses both count() (requires Countable) and foreach (requires Traversable). ArrayObject implements both interfaces.


iterable and callable

<?php
function total(iterable $numbers): int
{
    $sum = 0;

    foreach ($numbers as $number) {
        $sum += $number;
    }

    return $sum;
}

function moreNumbers(): Generator
{
    yield 4;
    yield 5;
}

function applyTwice(callable $operation, int $value): int
{
    return $operation($operation($value));
}

echo total([1, 2, 3]) . "\n";
echo total(moreNumbers()) . "\n";
echo applyTwice(fn(int $x) => $x * 3, 2) . "\n";

Output

6
9
18
  • iterable accepts both arrays and generators, so total() works with either. Generators are covered in PHP Generators.
  • callable accepts anything that can be called. applyTwice() calls the given function twice: 3 × (3 × 2) = 18.

mixed

mixed explicitly means "any type, including null". It is equivalent to the union of all types.

function debugValue(mixed $value): string
{
    return get_debug_type($value) . ': ' . var_export($value, true);
}

Use mixed when a function genuinely accepts anything (debugging helpers, generic containers). Prefer a more specific type whenever possible, because mixed gives tools and readers little information. See Mixed and Never Types.


Return Types

Return types are written after the parameter list:

Return TypeMeaning
: int, : string, …Returns that type
: ?UserReturns a User object or null
: int|falseReturns an integer or false
: voidReturns nothing
: neverNever returns (throws or exits)
: staticReturns an instance of the class the method was called on (PHP 8.0+, used in classes)

Return types are covered in Return Values.


Practical Example: A Typed Price Calculator

<?php
declare(strict_types=1);

function priceWithDiscount(
    float $price,
    int $quantity,
    ?float $discountPercent = null,
): float {
    if ($quantity < 1) {
        throw new InvalidArgumentException('Quantity must be at least 1.');
    }

    $total = $price * $quantity;

    if ($discountPercent !== null) {
        $total -= $total * $discountPercent / 100;
    }

    return round($total, 2);
}

echo priceWithDiscount(499.0, 2) . "\n";
echo priceWithDiscount(499.0, 2, 10.0) . "\n";
echo priceWithDiscount(250, 4, 5.0) . "\n";

try {
    echo priceWithDiscount(499.0, '2');
} catch (TypeError $e) {
    echo "TypeError: quantity must be an int\n";
}

Output

998
898.2
950
TypeError: quantity must be an int

Step-by-Step Explanation

  1. Strict mode ensures values arrive with exactly the declared types, except that integers are accepted for float parameters (priceWithDiscount(250, 4, 5.0) works).
  2. ?float $discountPercent = null makes the discount optional and uses null to mean "no discount".
  3. Types guarantee the kind of value, but not its range; the $quantity < 1 check enforces a business rule that the type alone cannot express.
  4. The string '2' is rejected in strict mode, so the bug in the caller is found immediately.

Common Mistakes

Confusing Nullable With Optional

Effect: function f(?string $x) still requires an argument.

Fix: Add a default: ?string $x = null.

Assuming Types Validate Business Rules

Effect: int $age accepts -5 and 5000.

Fix: Validate ranges and formats inside the function (or use value objects).

Using mixed or No Type Everywhere

Effect: Errors surface late, and tools cannot help.

Fix: Use the most specific type that fits.

Expecting Strict Behavior Without strict_types

Effect: "5" is silently accepted for an int parameter.

Fix: Add declare(strict_types=1); at the top of the calling file if strict checking is wanted.

Implicitly Nullable Parameters

Effect: string $x = null is deprecated in PHP 8.4.

Fix: Write ?string $x = null.


Best Practices

  • Declare types for every parameter and return value.
  • Prefer specific types and interfaces over mixed.
  • Use nullable and union types to describe real possibilities honestly.
  • Combine types with validation for value ranges and formats.
  • Consider enabling declare(strict_types=1); in all new files.

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

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting
Ask about this post
AI Ask about this post

Ask questions about Type Declarations in Functions and get answers drawn from it.

Signed-in readers only.