Type Declarations

Type Declarations, Union, Intersection and never

Beyond the scalar and nullable types of Strict Types and Null and Nullables, a union int|string accepts either type, and an intersection Countable&ArrayAccess demands an object implementing every listed interface; a DNF type such as (A&B)|null combines the two. never means the function always throws or exits, so analyzers (Debugging and Analysis) treat code after the call as dead.

When each type form arrived
Type form Example Since
Union, mixed, static int|string, mixed 8.0
Intersection, never A&B, : never 8.1
DNF; null, false, true alone (A&B)|null, : false 8.2
Union, intersection and never types, with the errors they raisePHP
<?php
declare(strict_types=1);
function formatId(int|string $id): string { return "ID-$id"; }
function size(Countable&ArrayAccess $c): int { return count($c); }
function fail(string $msg): never { throw new RuntimeException($msg); }
echo formatId(42), ' ', formatId('A100'), ' ', size(new ArrayObject([1, 2])), "\n";
foreach ([fn() => formatId(1.5), fn() => size(new SplMinHeap()), fn() => fail('disk full')]
  as $call) {
  try {
    $call();
  } catch (TypeError | RuntimeException $e) {
    echo strstr($e->getMessage(), ', called', true) ?: $e->getMessage(), "\n";
  }
}
Output
ID-42 ID-A100 2
formatId(): Argument #1 ($id) must be of type string|int, float given
size(): Argument #1 ($c) must be of type Countable&ArrayAccess, SplMinHeap given
disk full

SplMinHeap is Countable but not ArrayAccess. The union prints as string|int: PHP normalizes types rather than echoing them as written.