match

(PHP 8)

match 表达式基于对值的同一性检查来分支评估。类似于 switch 语句,match 表达式有一个与多个备选方案进行比较的主题表达式。与 switch 不同,它将像三元表达式一样评估为一个值。与 switch 不同,比较是同一性检查 (===) 而不是弱相等检查 (==)。Match 表达式从 PHP 8.0.0 开始可用。

示例 #1 match 表达式的结构

<?php
$return_value
= match (subject_expression) {
single_conditional_expression => return_expression,
conditional_expression1, conditional_expression2 => return_expression,
};
?>

示例 #2 基本的 match 用法

<?php
$food
= 'cake';

$return_value = match ($food) {
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
};

var_dump($return_value);
?>

上面的示例将输出

string(19) "This food is a cake"

示例 #3 使用 match 与比较运算符的示例

<?php
$age
= 18;

$output = match (true) {
$age < 2 => "Baby",
$age < 13 => "Child",
$age <= 19 => "Teenager",
$age > 19 => "Young adult",
$age >= 40 => "Old adult"
};

var_dump($output);
?>

上面的示例将输出

string(8) "Teenager"

注意: match 表达式的结果不需要使用。

注意: match 表达式必须以分号 ; 结束。

match 表达式类似于 switch 语句,但有一些关键区别

  • match 分支比较值严格 (===),而不是像 switch 语句那样松散。
  • match 表达式返回一个值。
  • match 分支不像 switch 语句那样贯穿到后面的情况。
  • match 表达式必须是穷举的。

switch 语句一样,match 表达式逐个分支执行。在开始时,不执行任何代码。只有在所有先前条件表达式未能与主题表达式匹配时才评估条件表达式。只有与匹配的条件表达式相对应的返回表达式将被评估。例如

<?php
$result
= match ($x) {
foo() => ...,
$this->bar() => ..., // $this->bar() isn't called if foo() === $x
$this->baz => beep(), // beep() isn't called unless $x === $this->baz
// etc.
};
?>

match 表达式分支可能包含用逗号分隔的多个表达式。这表示逻辑或,是具有相同右手边的多个匹配分支的简写。

<?php
$result
= match ($x) {
// This match arm:
$a, $b, $c => 5,
// Is equivalent to these three match arms:
$a => 5,
$b => 5,
$c => 5,
};
?>

一个特例是 default 模式。此模式与之前未匹配的任何内容匹配。例如

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

注意: 多个 default 模式将引发 E_FATAL_ERROR 错误。

match 表达式必须是穷举的。如果主题表达式未被任何匹配分支处理,则会抛出 UnhandledMatchError

示例 #4 未处理的匹配表达式的示例

<?php
$condition
= 5;

try {
match (
$condition) {
1, 2 => foo(),
3, 4 => 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 作为主题表达式。

示例 #5 使用广义匹配表达式来分支到整数范围

<?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"

示例 #6 使用广义匹配表达式来分支到字符串内容

<?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"
添加注释

用户贡献注释 9 个注释

darius dot restivan at gmail dot com
3 年前
这将允许使用更漂亮的 FizzBuzz 解决方案

<?php

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

for (
$i = 0; $i <=100; $i++)
{
fizzbuzz($i);
}
匿名
2 年前
<?php
function days_in_month(string $month, $year): int
{
return match(
strtolower(substr($month, 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"),
};
}
?>

可以更简洁地写成

<?php
function days_in_month(string $month, $year): int
{
return match(
strtolower(substr($month, 0, 3))) {
'apr', 'jun', 'sep', 'nov' => 30,
'jan', 'mar', 'may', 'jul', 'aug', 'oct', 'dec' => 31,
'feb' => is_leap($year) ? 29 : 28,
default => throw new
InvalidArgumentException("Bogus month"),
};
}
?>
Hayley Watson
3 年前
除了类似于 switch 之外,match 表达式还可以被认为是增强的查找表——用于简单数组查找不足以处理边缘情况,但完整的 switch 语句会过于臃肿。

对于一个熟悉的例子,以下
<?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");
}

?>

结尾处那些繁琐的代码可以替换为

<?php
function days_in_month(string $month): int
{
return match(
strtolower(substr($month, 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"),
};
}
?>

这也利用了 PHP 8.0 中的 "throw" 被作为表达式而不是语句处理。
Sbastien
1 年前
我使用 match 而不是存储 PDOStatement::rowCount() 的结果并链接 if/elseif 条件或使用丑陋的 switch/break

<?php

$sql
= <<<SQL
INSERT INTO ...
ON DUPLICATE KEY UPDATE ...
SQL;

$upkeep = $pdo->prepare($sql);

$count_untouched = 0;
$count_inserted = 0;
$count_updated = 0;

foreach (
$data as $record) {
$upkeep->execute($record);
match (
$upkeep->rowCount()) {
0 => $count_untouched++,
1 => $count_inserted++,
2 => $count_updated++,
};
}

echo
"未受影响的行数: "{$count_untouched}\r\n";
echo
"插入的行数: "{$count_inserted}\r\n";
echo
"更新的行数: "{$count_updated}\r\n";
thomas at zuschneid dot de
1 年前
虽然 `match` 允许使用 `","` 连接多个条件,例如
<?php
$result
= match ($source) {
cond1, cond2 => val1,
default =>
val2
};
?>
但使用 `default` 连接条件似乎无效,例如
<?php
$result
= match ($source) {
cond1 => val1,
cond2, default => val2
};
?>
tolga dot ulas at tolgaulas dot com
5 个月前
是的,目前不支持代码块,但这个技巧有效

match ($foo){
'bar'=>(function(){
echo "bar";
})(),
default => (function(){
echo "baz";
})()
};
php at joren dot dev
2 年前
如果您想在匹配条件表达式时执行多个返回值表达式,您可以将所有返回值表达式放在数组中。

<?php
$countries
= ['Belgium', 'Netherlands'];
$spoken_languages = [
'Dutch' => false,
'French' => false,
'German' => false,
'English' => false,
];

foreach (
$countries as $country) {
match(
$country) {
'Belgium' => [
$spoken_languages['Dutch'] = true,
$spoken_languages['French'] = true,
$spoken_languages['German'] = true,
],
'Netherlands' => $spoken_languages['Dutch'] = true,
'Germany' => $spoken_languages['German'] = true,
'United Kingdom' => $spoken_languages['English'] = true,
};
}

var_export($spoken_languages);
// array ( 'Dutch' => true, 'French' => true, 'German' => true, 'English' => false, )

?>
mark at manngo dot net
2 年前
虽然您无法为语言结构添加 polyfill,但可以使用简单的数组模拟基本行为。

使用上面的示例 2

<?php
$food
= 'apple';
$return_value = match ($food) {
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
};
print
$return_value;
?>

…您可以使用类似的方法

<?php
$food
= 'apple';
$return_value = [
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
][
$food];
print
$return_value;
?>
tm
2 年前
如果您如上所述使用 `match` 表达式进行非恒等性检查,请确保您使用的内容在成功时确实返回 `true`。

通常,您在使用 `if` 条件时依赖于真值和假值,但这对 `match` 不起作用(例如 `preg_match`)。强制转换为布尔值将解决此问题。
To Top