Take the State of PHP 2026 Survey →

Match expression v2 View as Markdown

Implemented (PHP 8.0)
Authors
Ilija Tovilo, tovilo.ilija@gmail.com
Date
2020-05-22
Target
PHP 8.0
Implementation
https://github.com/php/php-src/pull/5371
Source
wiki.php.net/rfc/match_expression_v2

Vote

Add match expressions to the language? closed
Yes
43
No
2
45 voters
alec No 2020-06-22 16:00
as Yes 2020-06-27 22:07
ashnazg Yes 2020-06-23 21:53
beberlei Yes 2020-06-22 06:52
bmajdak Yes 2020-06-19 15:23
brzuchal Yes 2020-06-19 09:19
carusogabriel Yes 2020-06-27 14:09
cpriest Yes 2020-06-20 21:01
danack Yes 2020-06-24 15:01
derick Yes 2020-06-26 09:58
dm Yes 2020-07-02 22:12
dmitry No 2020-06-22 08:46
duodraco Yes 2020-06-25 17:37
galvao Yes 2020-06-22 21:31
ilutov Yes 2020-06-19 08:59
jasny Yes 2020-06-24 10:07
jbnahan Yes 2020-06-22 11:45
kalle Yes 2020-06-19 10:35
kelunik Yes 2020-06-28 06:25
kguest Yes 2020-06-22 09:17
kocsismate Yes 2020-06-19 11:10
lcobucci Yes 2020-06-27 06:35
marandall Yes 2020-06-19 14:34
mariano Yes 2020-07-02 00:36
mgocobachi Yes 2020-07-01 18:07
nicolasgrekas Yes 2020-06-23 20:44
ocramius Yes 2020-06-19 09:09
pajoye Yes 2020-06-20 05:11
pollita Yes 2020-06-21 07:39
ralphschindler Yes 2020-06-21 12:52
rdohms Yes 2020-06-23 12:24
reywob Yes 2020-06-24 08:15
santiagolizardo Yes 2020-06-23 13:35
seld Yes 2020-06-19 12:47
sergey Yes 2020-06-23 13:09
sirsnyder Yes 2020-06-19 14:58
stas Yes 2020-06-19 18:09
tandre Yes 2020-06-19 13:31
theodorejb Yes 2020-06-19 10:44
theseer Yes 2020-06-21 23:07
thijs Yes 2020-06-26 09:11
till Yes 2020-07-02 08:56
weierophinney Yes 2020-06-25 19:14
wyrihaximus Yes 2020-06-23 12:30
yunosh Yes 2020-07-02 19:26

Discussions on the mailing list

[RFC][DISCUSSION] Match expression v2
by Ilija Tovilo · 24 messages
[VOTE] Match expression v2
by Ilija Tovilo · 2 messages
Allow dropping (true) from match expression conditions in 8.1
by tyson andre · 1 message
Making "match" catch exceptions
· 5 messages

RFC text

Copied from wiki.php.net, last synchronized Oct 13, 2026 11:51 UTC

Proposal

This RFC proposes adding a new match expression that is similar to switch but with safer semantics and the ability to return values.

From the Doctrine query parser:

// Before
switch ($this->lexer->lookahead['type']) {
    case Lexer::T_SELECT:
        $statement = $this->SelectStatement();
        break;
 
    case Lexer::T_UPDATE:
        $statement = $this->UpdateStatement();
        break;
 
    case Lexer::T_DELETE:
        $statement = $this->DeleteStatement();
        break;
 
    default:
        $this->syntaxError('SELECT, UPDATE or DELETE');
        break;
}
 
// After
$statement = match ($this->lexer->lookahead['type']) {
    Lexer::T_SELECT => $this->SelectStatement(),
    Lexer::T_UPDATE => $this->UpdateStatement(),
    Lexer::T_DELETE => $this->DeleteStatement(),
    default => $this->syntaxError('SELECT, UPDATE or DELETE'),
};

Differences to switch

Return value

It is very common that the switch produces some value that is used afterwards.

switch (1) {
    case 0:
        $result = 'Foo';
        break;
    case 1:
        $result = 'Bar';
        break;
    case 2:
        $result = 'Baz';
        break;
}
 
echo $result;
//> Bar

It is easy to forget assigning $result in one of the cases. It is also visually unintuitive to find $result declared in a deeper nested scope. match is an expression that evaluates to the result of the executed arm. This removes a lot of boilerplate and makes it impossible to forget assigning a value in an arm.

echo match (1) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
};
//> Bar

No type coercion

The switch statement loosely compares (==) the given value to the case values. This can lead to some very surprising results.

switch ('foo') {
    case 0:
      $result = "Oh no!\n";
      break;
    case 'foo':
      $result = "This is what I expected\n";
      break;
}
echo $result;
//> Oh no!

The match expression uses strict comparison (===) instead. The comparison is strict regardless of strict_types.

echo match ('foo') {
    0 => "Oh no!\n",
    'foo' => "This is what I expected\n",
};
//> This is what I expected

No fallthrough

The switch fallthrough has been a large source of bugs in many languages. Each case must explicitly break out of the switch statement or the execution will continue into the next case even if the condition is not met.

switch ($pressedKey) {
    case Key::RETURN_:
        save();
        // Oops, forgot the break
    case Key::DELETE:
        delete();
        break;
}

The match expression resolves this problem by adding an implicit break after every arm.

match ($pressedKey) {
    Key::RETURN_ => save(),
    Key::DELETE => delete(),
};

Multiple conditions can be comma-separated to execute the same block of code.

echo match ($x) {
    1, 2 => 'Same for 1 and 2',
    3, 4 => 'Same for 3 and 4',
};

Exhaustiveness

Another large source of bugs is not handling all the possible cases supplied to the switch statement.

switch ($operator) {
    case BinaryOperator::ADD:
        $result = $lhs + $rhs;
        break;
}
 
// Forgot to handle BinaryOperator::SUBTRACT

This will go unnoticed until the program crashes in a weird way, causes strange behavior or even worse becomes a security hole. match throws an UnhandledMatchError if the condition isn’t met for any of the arms. This allows mistakes to be caught early on.

$result = match ($operator) {
    BinaryOperator::ADD => $lhs + $rhs,
};
 
// Throws when $operator is BinaryOperator::SUBTRACT

Miscellaneous

Arbitrary expressions

A match condition can be any arbitrary expression. Analogous to switch each condition will be checked from top to bottom until the first one matches. If a condition matches the remaining conditions won’t be evaluated.

$result = match ($x) {
    foo() => ...,
    $this->bar() => ..., // bar() isn't called if foo() matched with $x
    $this->baz => ...,
    // etc.
};

Future scope

Blocks

In this RFC the body of a match arm must be an expression. Blocks for match and arrow functions will be discussed in a separate RFC.

Pattern matching

I have experimented with pattern matching and decided not to include it in this RFC. Pattern matching is a complex topic and requires a lot of thought. Each pattern should be discussed in detail in a separate RFC.

Allow dropping (true)

$result = match { ... };
// Equivalent to
$result = match (true) { ... };

Backward Incompatible Changes

match was added as a keyword (reserved_non_modifiers). This means it can’t be used in the following contexts anymore:

  • namespaces
  • class names
  • function names
  • global constants

Note that it will continue to work in method names and class constants.

Syntax comparison

https://gist.github.com/iluuu1994/11ac292cf7daca8162798d08db219cd5

Vote

Voting starts 2020-06-19 and ends 2020-07-03.

As this is a language change, a 2/3 majority is required.

Vote: Add match expressions to the language? — Yes: 43 · No: 2 (closed)