Type Declarations & Return Types
PHP started life as an untyped, "anything goes" language, but modern PHP lets you declare exactly what type a function parameter should accept and what type it should return. These are called type declarations (sometimes "type hints"), and while they are optional, using them turns a whole category of bugs — passing the wrong kind of value into a function — into an immediate, obvious error instead of a subtle runtime surprise several calls later.
Scalar type hints on parameters
You can prefix a parameter with a type such as int, float, string, or bool, and PHP will enforce it every time the function is called. By default PHP is somewhat forgiving here: it will coerce compatible values rather than reject them outright.
Scalar type hints with default coercion
<?php
function repeat(string $text, int $times): string
{
return str_repeat($text, $times);
}
echo repeat("ab", 3);
echo "\n";
// "5" is a numeric string - PHP coerces it to int(5) here
echo repeat("x", "5");ababab xxxxx
Return type declarations
Adding : type after the parameter list declares what the function promises to hand back. This is checked the same way as parameter types — PHP will coerce a compatible value to match, or raise a TypeError if it truly cannot.
A return type of int
<?php
function average(array $numbers): float
{
return array_sum($numbers) / count($numbers);
}
var_dump(average([2, 4, 6]));float(4)
Nullable types
Sometimes a parameter or return value can legitimately be either a specific type or null — for example, a "find user by id" function that returns null when nothing matches. Prefixing the type with a question mark, like ?int or ?string, declares that null is an acceptable value alongside the base type.
A nullable return type
<?php
function findUserName(int $id): ?string
{
$users = [1 => "Amara", 2 => "Ben"];
return $users[$id] ?? null;
}
var_dump(findUserName(1));
var_dump(findUserName(99));string(5) "Amara" NULL
The void return type
void is a special return type for functions whose purpose is a side effect — logging, printing, mutating something — rather than producing a value. A void function is not allowed to return anything with a value (a bare return; to exit early is still fine), and calling code should not try to use its result.
void signals no meaningful return value
<?php
function printReceipt(string $item, float $price): void
{
echo "{$item}: \$" . number_format($price, 2) . "\n";
return; // fine - returning early with no value
}
printReceipt("Coffee", 4.5);Coffee: $4.50
declare(strict_types=1)
By default, PHP runs in weak/coercive typing mode, silently converting compatible scalar values — "5" becomes int(5), 1 becomes float(1.0), and so on — to match a declared type. Adding declare(strict_types=1); at the very top of a file switches that file's calls to strict mode: scalar type mismatches on parameters and return values throw a TypeError instead of being coerced. Note that strict mode still allows an int to widen to a float parameter, since no data is lost, but it will not accept a string where an int is declared even if that string looks numeric.
strict_types turns coercion into a TypeError
<?php
declare(strict_types=1);
function double(int $n): int
{
return $n * 2;
}
echo double(4);
echo "\n";
try {
echo double("4"); // no longer coerced - this is now an error
} catch (TypeError $e) {
echo "Caught: " . $e->getMessage();
}8 Caught: double(): Argument #1 ($n) must be of type int, string given, called in ...
Union types
When a parameter or return value can genuinely be one of several unrelated types, you can declare a union type by separating them with a pipe, such as int|string. This is different from mixed, which accepts literally anything — a union type still restricts the value to the listed options.
A union type parameter
<?php
function formatId(int|string $id): string
{
return "ID-" . $id;
}
echo formatId(42);
echo "\n";
echo formatId("A9");ID-42 ID-A9
Type declarations are optional on both parameters and return values; omitting one leaves that spot dynamically typed.
Object/class types (e.g. a parameter typed as
DateTime) are always checked strictly, even withoutstrict_types- coercion only applies to scalar types.selfandstaticare valid return types inside a class, meaning "an instance of this class."A
TypeErroris a genuine PHPError, not anException- you must catchErrororThrowable(not justException) if you want to handle it.