match

(PHP 8)

match 式は、値の一致をチェックした結果に基づいて評価結果を分岐します。 switch 文と似ていますが、 match 式は複数の候補と比較される制約式を持ちます。 switch 文とは異なり、 三項演算子のように値を評価します。 switch 文とは異なり、 弱い比較(==)ではなく、 型と値の一致チェック(===) に基づいて行われます。 match 式は PHP 8.0.0 以降で利用可能です。

例1 match 式の構造

<?php
$return_value 
match (制約式) {
    
単一の条件式 => 返却式,
    
条件式1条件式2 => 返却式,
};
?>

注意: match 式の結果は、必ずしも使う必要はありません。

注意: match 式は、必ずセミコロン ; で終わらなければなりません。

match 式は、 switch 文と似ていますが、いくつかの違いがあります:

  • match 式の比較は、 switch 文が行う弱い比較ではなく、 厳密に値を比較(===) します。
  • match 式は値を返します。
  • match 式の分岐は、 switch 文のように後の分岐に抜けたりはしません。
  • match 式は、全ての場合を網羅していなければいけません。

switch 文のように、 match 式はマッチさせる分岐をひとつひとつ実行します。 はじめは、コードは何も実行されません。 以前のすべての条件式が、制約式とマッチしなかった場合に条件式が実行されます。 条件式に一致する式が評価された場合に、返却式が評価されます。 たとえば、以下のようになります:

<?php
$result 
match ($x) {
    
foo() => ...,
    
$this->bar() => ..., // foo() === $x であれば $this->bar() は呼び出されません。
    
$this->baz => beep(), // $x === $this->baz でなければ beep() は呼び出されません。
    // などなど
};
?>

match 式の分岐は、複数の式をカンマ区切りで含めても構いません。 これは論理ORであり、複数の分岐の右辺を同じにする場合の短縮記法です。

<?php
$result 
match ($x) {
    
// この分岐は:
    
$a$b$c => 5,
    
// 以下の3つの分岐と等しい:
    
$a => 5,
    
$b => 5,
    
$c => 5,
};
?>

default パターンという特別な場合があります。 このパターンは前の分岐にマッチしなかったあらゆる場合にマッチします。 たとえば、以下のようになります:

<?php
$expressionResult 
match ($condition) {
    
1=> foo(),
    
3=> bar(),
    default => 
baz(),
};
?>

注意: 複数の default パターンがあった場合、 E_FATAL_ERROR が発生します。

match 式は、全ての場合を網羅していなければいけません。 制約式がどの分岐でも処理できなかった場合、 UnhandledMatchError がスローされます。

例2 処理されない match 式の例

<?php
$condition 
5;

try {
    
match ($condition) {
        
1=> foo(),
        
3=> bar(),
    };
} catch (\
UnhandledMatchError $e) {
    
var_dump($e);
}
?>

上の例の出力は以下となります。

object(UnhandledMatchError)#1 (7) {
  ["message":protected]=>
  string(33) "Unhandled match value of type int"
  ["string":"Error":private]=>
  string(0) ""
  ["code":protected]=>
  int(0)
  ["file":protected]=>
  string(9) "/in/ICgGK"
  ["line":protected]=>
  int(6)
  ["trace":"Error":private]=>
  array(0) {
  }
  ["previous":"Error":private]=>
  NULL
}

厳密な一致チェックを行わずに match 式を使う

制約式に true を指定することで、 厳密な一致チェックを行わずに match 式を使うことができます。

例3 整数の範囲に応じてmatch式を分岐させる一般的な使い方

<?php

$age 
23;

$result match (true) {
    
$age >= 65 => 'senior',
    
$age >= 25 => 'adult',
    
$age >= 18 => 'young adult',
    default => 
'kid',
};

var_dump($result);
?>

上の例の出力は以下となります。

string(11) "young adult"

例4 文字列の内容に応じてmatch式を分岐させる一般的な使い方

<?php

$text 
'Bienvenue chez nous';

$result match (true) {
    
str_contains($text'Welcome') || str_contains($text'Hello') => 'en',
    
str_contains($text'Bienvenue') || str_contains($text'Bonjour') => 'fr',
    
// ...
};

var_dump($result);
?>

上の例の出力は以下となります。

string(2) "fr"
add a note add a note

User Contributed Notes 4 notes

up
20
Hayley Watson
3 years ago
As well as being similar to a switch, match expressions can be thought of as enhanced lookup tables — for when a simple array lookup isn't enough without extra handling of edge cases, but a full switch statement would be overweight.

For a familiar example, the following
<?php

function days_in_month(string $month): int
{
    static
$lookup = [
   
'jan' => 31,
   
'feb' => 0,
   
'mar' => 31,
   
'apr' => 30,
   
'may' => 31,
   
'jun' => 30,
   
'jul' => 31,
   
'aug' => 31,
   
'sep' => 30,
   
'oct' => 31,
   
'nov' => 30,
   
'dec' => 31
   
];

   
$name = strtolower(substr($name, 0, 3));

    if(isset(
$lookup[$name])) {
        if(
$name == 'feb') {
            return
is_leap($year) ? 29 : 28;
        } else {
            return
$lookup[$name];
        }
    }
    throw new
InvalidArgumentException("Bogus month");
}

?>

with the fiddly stuff at the end, can be replaced by

<?php
function days_in_month(string $month): int
{
    return
match(strtolower(substr($name, 0, 3))) {
       
'jan' => 31,
       
'feb' => is_leap($year) ? 29 : 28,
       
'mar' => 31,
       
'apr' => 30,
       
'may' => 31,
       
'jun' => 30,
       
'jul' => 31,
       
'aug' => 31,
       
'sep' => 30,
       
'oct' => 31,
       
'nov' => 30,
       
'dec' => 31,
        default => throw new
InvalidArgumentException("Bogus month"),
    };
}
?>

Which also takes advantage of "throw" being handled as of PHP 8.0 as an expression instead of a statement.
up
3
darius dot restivan at gmail dot com
3 years ago
This will allow for a nicer FizzBuzz solution:

<?php

function fizzbuzz($num) {
    print
match (0) {
       
$num % 15 => "FizzBuzz" . PHP_EOL,
       
$num % => "Fizz" . PHP_EOL,
       
$num % => "Buzz" . PHP_EOL,
        default   =>
$num . PHP_EOL,
    };
}

for (
$i = 0; $i <=100; $i++)
{
   
fizzbuzz($i);
}
up
3
lewiscowles at me dot com
3 years ago
The comment correcting Hayley's example misses that year is also present in the example, but not a function argument.

Most code from PHP.net should not be copied without user care. It was nonetheless, a great example of using short syntax to reduce complexity.
up
8
webmaster at warkensoft dot com
3 years ago
In the "familiar example" presented by Hayley Watson the following code is incorrect:

strtolower(substr($name, 0, 3))

It should instead be written in both instances as:

strtolower(substr($month, 0, 3))
To Top