Skip to content
Theory · Conditions · lesson 7 of 8

The match expression

switch chooses which lines to run. But more often you need a value out of the choice: the name of a day, a price, the label on a button. With switch you have to assign it in every branch and remember the break. For this kind of choice, PHP 8 added the match expression.

Back to the corridor with doors. The doorman of match is stricter: he checks the number on the ticket character for character, and a handwritten 7 is not the same to him as a printed number. He does not let you into the corridor; he hands you a slip with the answer right away—a value.

PHP
<?php

$day = 7;
$kind = match ($day) {
    6, 7 => 'weekend',
    default => 'workday',
};
echo $kind, "\n"; // weekend

Let's take it apart:

  • match ($day): the value that gets compared;
  • 6, 7 => 'weekend': an arm, with values separated by commas on the left and the result on the right;
  • default => 'workday': the arm for all other values;
  • the semicolon after } is needed because the whole match sits inside an assignment.

$day is 7 and matches a value of the first arm, so match gives 'weekend'. That value goes into $kind, and echo prints it.

Expression and statement

An expression is code that has a value: 2 + 3, $age >= 18, the ternary operator. A statement, as you already know, is a command of the program; it performs an action but gives no value: echo, if, switch. match is an expression, so you can assign it, pass it to echo or return it from a function with return.

The other differences from switch follow from that:

  • a match arm holds one expression, not a set of lines;
  • arms do not fall through: exactly one runs, and no break is needed;
  • several values with one result are written with commas, not as labels in a row.

Strict comparison

match compares the value with the arms using ===: both the type and the value must match.

PHP
$input = '7';
$label = match ($input) {
    7 => 'number',
    '7' => 'string',
    default => 'unknown',
};
echo $label, "\n"; // string

Let's go through it step by step:

  1. $input holds the string '7'.
  2. match skips the arm 7: the string '7' is not identical to the number 7.
  3. The arm '7' matches—it is the same string of the same type, and match gives 'string'.

In place of match, switch would pick the first arm, because '7' == 7 is true.

When no arm fits

switch with no matching label silently does nothing. match cannot do that, because an expression must give a value. If no arm fits and there is no default, match throws an UnhandledMatchError, and the program stops. For example, with $day = 9 and the single arm 6, 7 => 'weekend', PHP writes:

TEXT
Fatal error: Uncaught UnhandledMatchError: Unhandled match case 9

This is where the doorman analogy ends. A real doorman with no matching door would shrug and let you go, while match raises the alarm and stops the whole program. That is useful: a forgotten case shows up at once, not a month later as an empty line in a report.

match (true) for ranges

Arms can check more than exact values. In match (true) the arms hold conditions on the left, and match looks for the first one that gives exactly true:

PHP
$score = 85;
$grade = match (true) {
    $score >= 90 => 'A',
    $score >= 75 => 'B',
    $score >= 50 => 'C',
    default => 'F',
};
echo $grade, "\n"; // B

The conditions are checked from top to bottom, as in an elseif chain: 85 >= 90 is false, 85 >= 75 is true, so $grade gets 'B'. The comparison is strict here too. An arm with a truthy value, such as the string 'Anna', does not match true: it takes a real true, the kind a comparison gives.

switch or match

Interviewers often ask: "How does match differ from switch?" The answer:

  • match is an expression and gives a value, while switch is a statement;
  • match compares with ===, while switch uses ==;
  • match has no fall-through, and no break is needed;
  • match with no matching arm throws an UnhandledMatchError, while switch silently does nothing.

switch still fits when a branch has to run several statements: a match arm is a single expression.

Common mistake

A page number came from the address bar as the string '2', while the values in the match arms are written as numbers:

PHP
$page = '2';
$title = match ($page) { // ✗
    1 => 'home',
    2 => 'about',
};
echo $title, "\n";
TEXT
Fatal error: Uncaught UnhandledMatchError: Unhandled match case '2'

The string '2' is not identical to the number 2, there is no default arm, and match throws an error. Cast the value to the right type before comparing: match ((int) $page).

Key points

  • match is an expression: it gives the value of the one matching arm, with no fall-through and no break.
  • Arms are compared with ===, so the string '2' does not match the number 2.
  • With no matching arm and no default, match throws an UnhandledMatchError.
  • match (true) picks the first arm whose condition gives true.