Skip to content

PHP Tutorial Modern PHP Features

Union, Nullable and Other Advanced Types

Lesson 223 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

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)

index.php
PHP
<?php
function title(?string $name): string
{
    return $name === null ? "Guest" : ucfirst($name);
}

echo title(null) . ", " . title("ana");
Output
OUTPUT
Guest, Ana

Union types: A|B (PHP 8.0)

index.php
PHP
<?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");
Output
OUTPUT
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.

index.php
PHP
<?php
function dump(mixed $value): string
{
    return get_debug_type($value);
}

echo dump([]) . " " . dump(1.5) . " " . dump(null);
Output
OUTPUT
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).

index.php
PHP
<?php
class Model
{
    public static function make(): static
    {
        return new static();
    }
}

class User extends Model {}

echo get_class(User::make());
Output
OUTPUT
User

never (PHP 8.1)

A function that never returns normally: it always throws or exits.

index.php
PHP
<?php
function abort(string $message): never
{
    throw new RuntimeException($message);
}

try {
    abort("stop");
} catch (RuntimeException $e) {
    echo $e->getMessage();
}
Output
OUTPUT
stop

void and null, false, true as types

  • void: returns nothing.
  • null, false and true can be used as standalone types from PHP 8.2. false is also useful inside unions (string|false) to describe functions such as strpos().
index.php
PHP
<?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"));
Output
OUTPUT
int(1)
bool(false)

Intersection types: A&B (PHP 8.1)

The value must satisfy both types. This is common with interfaces.

index.php
PHP
<?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());
Output
OUTPUT
data

DNF types (PHP 8.2)

You can combine them: (A&B)|null. Wrap the intersection in brackets. This is “disjunctive normal form”.

index.php
PHP
<?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);
Output
OUTPUT
7: Pen / nothing

Which type should I write?

If the value is…Use
Sometimes missing?Type
One of a few fixed typesA|B
Truly anythingmixed
An object that must offer several abilitiesA&B
An exit that never returnsnever

Keep unions short. A parameter that accepts five types is usually a design problem.

lightbulb
Narrow it early
Convert a union to a single type at the start of a function, so the rest of the code works with one type.