PHP’s type system has grown. Here is the full set of tools you can use in declarations.
Nullable: ?Type
?string means “a string or null”. It is the same as string|null. (PHP 7.1)
<?php
function title(?string $name): string
{
return $name === null ? "Guest" : ucfirst($name);
}
echo title(null) . ", " . title("ana");
Guest, Ana
Union types: A|B (PHP 8.0)
<?php
function format(int|float|string $value): string
{
return match (true) {
is_int($value) => "int:$value",
is_float($value) => "float:$value",
default => "string:$value",
};
}
echo format(5) . " " . format(2.5) . " " . format("x");
int:5 float:2.5 string:x
mixed (PHP 8.0)
mixed means any type at all (including null). It tells readers you did not narrow the type, which is better than writing no type.
<?php
function dump(mixed $value): string
{
return get_debug_type($value);
}
echo dump([]) . " " . dump(1.5) . " " . dump(null);
array float null
static, self and the class types
self is the class itself, static is the class that was called (PHP 8.0 added static as a return type).
<?php
class Model
{
public static function make(): static
{
return new static();
}
}
class User extends Model {}
echo get_class(User::make());
User
never (PHP 8.1)
A function that never returns normally: it always throws or exits.
<?php
function abort(string $message): never
{
throw new RuntimeException($message);
}
try {
abort("stop");
} catch (RuntimeException $e) {
echo $e->getMessage();
}
stop
void and null, false, true as types
void: returns nothing.null,falseandtruecan be used as standalone types from PHP 8.2.falseis also useful inside unions (string|false) to describe functions such asstrpos().
<?php
function find(array $items, string $needle): int|false
{
$key = array_search($needle, $items, true);
return $key;
}
var_dump(find(["a", "b"], "b"));
var_dump(find(["a", "b"], "z"));
int(1)
bool(false)
Intersection types: A&B (PHP 8.1)
The value must satisfy both types. This is common with interfaces.
<?php
interface Reader { public function read(): string; }
interface Closer { public function close(): void; }
class Stream implements Reader, Closer
{
public function read(): string { return "data"; }
public function close(): void {}
}
function consume(Reader&Closer $source): string
{
$data = $source->read();
$source->close();
return $data;
}
echo consume(new Stream());
data
DNF types (PHP 8.2)
You can combine them: (A&B)|null. Wrap the intersection in brackets. This is “disjunctive normal form”.
<?php
interface HasId { public function id(): int; }
interface HasName { public function name(): string; }
class Item implements HasId, HasName
{
public function id(): int { return 7; }
public function name(): string { return "Pen"; }
}
function show((HasId&HasName)|null $thing): string
{
return $thing === null ? "nothing" : $thing->id() . ": " . $thing->name();
}
echo show(new Item()) . " / " . show(null);
7: Pen / nothing
Which type should I write?
| If the value is… | Use |
|---|---|
| Sometimes missing | ?Type |
| One of a few fixed types | A|B |
| Truly anything | mixed |
| An object that must offer several abilities | A&B |
| An exit that never returns | never |
Keep unions short. A parameter that accepts five types is usually a design problem.