Type Declarations in Functions

Yo Yogesh.M Updated 17 Oct 2026
8 min read ·Lesson 51 of 248
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 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 User-defined Functions Slicing Strings Function Arguments 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 Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer PHP Form Handling PDO Transactions PDO Error Handling PHP XML Parsers PHP SimpleXML Parser PHP SimpleXML - Get PHP XML Expat Parser PHP DOM Parser PHP XMLReader PHP XMLWriter AJAX Intro PHP Class Constants Encryption with Sodium Recursive Functions Variable Functions Anonymous Functions Closures Arrow Functions First-class Callable Syntax PHP Arrays Indexed Arrays Associative Arrays Create Arrays Variables Scope Static Variables Variable Variables PHP Echo / Print PHP printf / sprintf PHP Data Types PHP Booleans PHP NULL PHP Strings String Functions array_map / array_filter / array_reduce Array Functions PHP Superglobals $GLOBALS What Is `$_SERVER` ? $_REQUEST $_POST $_GET $_FILES $_COOKIE PHP Type Juggling MySQL Order By MySQL Delete Data MySQL Update Data MySQL Limit Data MySQL Joins with PHP Pagination with PHP PDO Introduction PDO Connect PDO Prepared Statements PDO Fetch Modes PSR-4 Autoloading Installing Extensions (PECL / PIE) OPcache & Performance PHP-FPM Project: Contact Form with Email Project: Login & Registration System Project: CRUD App with MySQL Project: Shopping Cart with Sessions Project: REST API with PHP DateTime Class Timezones PHP Include Include vs Require PHP File Handling PHP File Open/Read PHP File Create/Write File Delete / Rename / Copy PHP Directories PHP CSV Files
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.