PHP types can say "this is an int" or "this is a string," but they can't say "this is one of these exact values." PHP already has a few types that stand for a single value: null, and since PHP 8.2, true and false. This RFC from Seifeddine Gmati extends that idea to specific numbers and strings.

Show me

function setLogLevel('debug'|'info'|'warning'|'error' $level): void {}
function setSign(-1|0|1 $sign): int {}

A value that isn't in the list throws a TypeError:

function f(1|2|3 $x): int { return $x; }

f(2); // int(2)
f(4); // TypeError: must be of type 1|2|3, int given

The RFC positions this for data whose shape is defined outside your code, like a JSON payload or an HTTP API. Enums are still the right tool for concepts your code owns.

How it works

  • Ints can be written in any form PHP already allows: 42, 0x2A, 0b101010, 1_000. A leading - or + works too, and 0x1 and 1 count as the same type.
  • Strings can use single or double quotes, but interpolated variables aren't allowed.
  • Floats like 1.5 or 4e3 are allowed, but INF and NAN are not.
  • Constants like PHP_INT_MAX can't be used. Only literal values count.

Floats are tricky because of rounding. 0.1 + 0.2 isn't exactly 0.3, so it would fail a 0.3 type, which is why float literals get their own vote.

A separate vote decides how values are matched. The author prefers strict matching, where the value must already have the right type in any mode, just like true and false work today:

f("2");  // TypeError, string given
f(2.0);  // TypeError, float given

The one exception is that an int can match a float literal, like 2 matching 2.0. The alternative, coercive matching, would convert "2" to 2 first when strict_types is off.

Other rules:

  • 1|int is an error, since int already covers 1.
  • Defaults must be in the list, so 1|2 $x = 3 is an error.
  • A child class can narrow return types and widen parameter types, like int to 1|2.
  • A new ReflectionLiteralScalarType class has a getValue() method.

Each value in a union is checked one at a time, so a very long list of values adds cost to every call.

What it means for existing code

Nothing breaks, since this is new syntax. Tools that parse PHP code, like IDEs and static analyzers, will need updates. So will code that handles reflection types, because of the new class.

Where it stands

The RFC is under discussion. It now targets PHP 8.7, though parts of the page still say 8.6. Three votes are set up: int and string literals, float literals, and the matching rule. The two type votes each need two-thirds, and the matching vote needs a simple majority. No votes have been cast.