PHP Type Declarations and Return Types

Declare input and output contracts, understand strict_types and separate language types from business validation.

Lesson 43 of 86Intermediate10 min read

Type declarations tell PHP and readers what kinds of values a function accepts and returns. function double(int $value): int says the input and result are integers. That contract can catch mismatches near their source, make editor suggestions more useful and reduce guessing when a function is called elsewhere.

Types do not express every rule. An integer can still be negative, a string can still be blank, and an array can contain the wrong record shape. This guide explains the language-level contract first, then shows how strict typing, nullable values and domain validation fit together in a small calculation.

Parameter and return declarations

Place a parameter type before its variable and a return type after the closing parameter parenthesis. Common types include int, float, string, bool, array, object, callable and iterable, plus class and interface names. A class declaration checks the object's type relationship, not whether it currently has valid domain state.

The return declaration checks the value produced by the function. A function declared to return int must actually return an acceptable integer result. Falling through without a return can violate that contract. A clear return type helps callers know whether they receive data, an object, or no useful value.

void describes a function that does not return a useful value. mixed allows any value and is available in PHP 8.0. Use mixed only when the API genuinely needs that flexibility; it removes much of the narrowing benefit. An array type does not constrain its element types, so document or validate the shape separately.

What strict_types changes

Place declare(strict_types=1); at the beginning of a PHP file, following the opening tag and permitted leading comments. It changes scalar coercion rules for calls made from that file. Without strict mode, compatible scalar values may be converted. With it, scalar mismatches are rejected more strictly; integers are still accepted for float parameters.

The calling file matters for user-defined function arguments. Declaring strict mode in a library does not force every loosely typed caller to become strict. Return values are governed by the function's declaration context. Understand this boundary when several files use different policies.

Strict types is not request validation and is not a global application setting. Form input commonly arrives as text, so parse it deliberately into a validated value before calling typed domain functions. A TypeError is useful for programmer mistakes, but it should not be the only explanation a public API gives for a malformed field.

Nullable, union and intersection types

?string permits string or null. The equivalent union is string|null. Use null when it has a clear meaning, such as no display name, and require callers to handle that possibility. Do not make every parameter nullable merely to avoid thinking about required input.

PHP 8.0 introduced union types such as int|float, which allow one of the listed types. PHP 8.1 introduced intersection types for objects that must satisfy several class/interface constraints. PHP 8.2 added more expressive combinations and standalone true, false and null types. These features have version requirements, so check the project's minimum runtime before adopting them.

never, available from PHP 8.1, describes a function that does not return normally because it always throws or terminates. It is different from void: a void function can finish and return control to the caller. A constructor-like factory returning an object should declare that object type instead of using a vague mixed contract.

Properties, inheritance and error handling

Typed properties have existed since PHP 7.4. A property declared without a default can be uninitialized, which is different from null. Reading it before initialization causes an error. Set required properties during construction or give them valid defaults; adding a type does not populate the value automatically.

Overridden methods must respect compatible type rules. Broadly, an implementation may accept a wider input contract and return a narrower result where the language's variance rules allow it. Do not casually change an override's types and assume inheritance will remain valid. Interfaces are especially useful for stating the behavior callers rely on.

When TypeError arises from a programming mismatch, fix the contract or caller rather than catching it everywhere and substituting empty values. At input boundaries, validate and report malformed data before reaching domain functions. The PHP type declarations reference documents exact supported forms and compatibility rules.

PHP Type Declarations and Return Types Examples

Each example shows the code, the output it produces and what each part does.

Example 1

State a numeric contract

Both values are integers, and the result is integer cents. The declaration prevents passing arbitrary arrays or objects. It does not reject a negative quantity or protect against every possible overflow, so a production price calculation needs domain limits in addition to these language types.

PHP
<?php
declare(strict_types=1);
function subtotal(int $quantity, int $unitCents): int {
    return $quantity * $unitCents;
}
echo subtotal(3, 250) . PHP_EOL;
Output
750
Example 2

Handle an optional name explicitly

The function explicitly handles null rather than assuming every caller supplies text. An empty string is still a string and would produce a different result. If blank names should also become guest, add that domain rule deliberately after considering whitespace and the accepted name format.

PHP
<?php
declare(strict_types=1);
function greeting(?string $name): string {
    return $name === null ? 'Hello, guest' : 'Hello, ' . $name;
}
echo greeting(null) . PHP_EOL;
echo greeting('Amina') . PHP_EOL;
Output
Hello, guest
Hello, Amina

PHP Type Declarations and Return Types in Real Projects

Practical task: a bounded line total

Write a function taking a quantity and unit price in integer cents. The quantity must be between one and one hundred, and the unit price must be between zero and one million cents. Those bounds keep the exercise's arithmetic modest and represent explicit product rules. Reject invalid values before multiplication.

PHP
<?php
declare(strict_types=1);
function lineTotal(int $quantity, int $unitCents): int {
    if ($quantity < 1 || $quantity > 100 || $unitCents < 0 || $unitCents > 1000000) {
        throw new InvalidArgumentException('Invalid line values');
    }
    return $quantity * $unitCents;
}
echo lineTotal(4, 125) . PHP_EOL;
Output
500

The type declarations state the representation; the condition states the permitted domain. Try a quantity of zero in a separate error-handling experiment and explain why it is rejected even though it is an integer. An HTTP handler should validate raw fields and translate the exception into its documented client response rather than exposing a stack trace.

PHP Type Declarations and Return Types FAQ

Does strict_types apply to the whole project?

No. It is a per-file declaration with important caller-boundary rules. Adopt a consistent project policy when possible.

Does array mean an array of strings?

No. It only constrains the outer type. Validate elements or use a documented collection abstraction.

Is nullable the same as optional?

No. A nullable parameter permits null. An optional parameter has a default and may be omitted. These can be combined but answer different questions.

Why is a typed property uninitialized?

Declaring its type does not necessarily assign a value. Initialize it before reading it.

Should types replace tests?

No. Types catch a class of mismatches. Tests still verify rules, boundary values, calculations and interactions that the type system does not express.

PHP Type Declarations and Return Types Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

A quantity is typed int but must be positive. What additional step is required?

PHP Type Declarations and Return Types Quiz

2 questions to check what you learned.

  1. What does : int after a function's parameter list declare?

    • The return type
    • The number of parameters
    • A global variable
  2. What does ?string permit?

    • Every scalar value
    • A string or null
    • Only an empty string