match Expression
| Since: | PHP 8.0(2020) |
|---|
To branch on multiple values and return the result as an expression, the 『match』 expression introduced in PHP 8 is useful. Similar to 『switch』, but it uses strict comparison (===) so no unintended type coercion occurs, and it can return a value directly for concise code.
Syntax
$variable = match (expression) {
value1 => return_value,
value2 => return_value,
value3, value4 => return_value, // multiple values can be grouped with commas
default => default_return_value,
};
Syntax Parts
| Syntax | Notes |
|---|---|
| match (expression) | Specifies the value to match against. Any expression can be used: variables, function return values, calculations, etc. |
| value => return_value | Each branch, called an "arm". When the match expression strictly matches (===) the arm's value, the right side is returned. |
| value3, value4 => return_value | Multiple values can be listed with commas. The right side is returned when any of the listed values match. |
| default | Optional. Specifies the value to return when no arm matches. If omitted and nothing matches, an exception is thrown (see below). |
Differences from switch
| Aspect | switch statement | match expression (PHP 8+) |
|---|---|---|
| Comparison | Loose comparison (==). Type coercion occurs. | Strict comparison (===). No type coercion. |
| Return value | Executes statements only; does not return a value. | Returns a value as an expression; can be assigned to a variable. |
| break required | Normally required at the end of each block. | Not required. Each arm is independent. |
| Fall-through | Occurs when 『break』 is omitted. | Does not occur. |
| Multiple conditions | Multiple 『case』 labels with fall-through. | Multiple values in one arm, separated by commas. |
| No match | If no 『default』, nothing executes. | If no 『default』 and nothing matches, 『UnhandledMatchError』 is thrown. |
Exception When No Arm Matches
When a 『match』 expression has no matching arm and no 『default』, an 『UnhandledMatchError』 is thrown. This design allows unexpected values to be caught early as bugs.
| Pattern | Behavior |
|---|---|
| With default | When no arm matches, the 『default』 value is returned. |
| No default, match found | The matched arm's value is returned. |
| No default, no match | An 『UnhandledMatchError』 exception is thrown. Unexpected values are detected as bugs. |
When unexpected values may be passed, adding 『default』 or catching the exception with 『try / catch』 are available options. For exception handling, see 『try / catch / finally』.
Sample Code
match_basic.php
<?php
// Determining the race of a Dragon Ball character
// Using match expression to assign a value to a variable
$characterName = "Goku";
$race = "Saiyan";
// match returns a value as an expression, so it can be assigned directly
$description = match($race) {
"Saiyan" => "A warrior race. Capable of transforming into Super Saiyan.",
"Namekian" => "From Planet Namek. Piccolo can fuse with others.",
"Android" => "Artificial humans developed by Dr. Gero.",
default => "Race unknown.",
};
echo $characterName . ": " . $description . "\n";
// Equivalent using switch (same behavior as the match expression above)
switch ($race) {
case "Saiyan":
$desc2 = "A warrior race. Capable of transforming into Super Saiyan.";
break;
case "Namekian":
$desc2 = "From Planet Namek. Piccolo can fuse with others.";
break;
case "Android":
$desc2 = "Artificial humans developed by Dr. Gero.";
break;
default:
$desc2 = "Race unknown.";
break;
}
echo $characterName . " (switch): " . $desc2 . "\n";
Running the code produces the following output:
php match_basic.php Goku: A warrior race. Capable of transforming into Super Saiyan. Goku (switch): A warrior race. Capable of transforming into Super Saiyan.
match_multi_conditions.php
<?php
// Grouping multiple values in a single arm using comma separation
// Determining the faction of Dragon Ball characters
$characters = ["Goku", "Vegeta", "Piccolo", "Frieza", "Cell", "Majin Buu"];
foreach ($characters as $name) {
$faction = match($name) {
// Multiple values can be grouped with commas (equivalent to switch fall-through)
"Goku", "Vegeta", "Piccolo" => "Z Warriors",
"Frieza", "Cell", "Majin Buu" => "Villain",
default => "Unknown",
};
echo $name . ": " . $faction . "\n";
}
Running the code produces the following output:
php match_multi_conditions.php Goku: Z Warriors Vegeta: Z Warriors Piccolo: Z Warriors Frieza: Villain Cell: Villain Majin Buu: Villain
match_strict_comparison.php
<?php
// match uses strict comparison (===)
// Checking the difference from switch's loose comparison
// Managing power level as an integer
$powerLevel = 0; // power level zero (latent ability unreleased)
echo "--- match (strict comparison) ---\n";
$result = match($powerLevel) {
0 => "Latent ability unreleased (strict match with integer 0)",
false => "false (type differs, no match)",
"" => "empty string (type differs, no match)",
default => "no match",
};
// match uses strict comparison, so integer 0 does not match false or empty string
echo "Result: " . $result . "\n";
echo "\n--- switch (loose comparison) ---\n";
switch ($powerLevel) {
case false:
// 0 == false is true, so this executes (unintended match)
echo "Result: matched false (unintended loose comparison match)\n";
break;
case 0:
echo "Result: matched integer 0\n";
break;
}
Running the code produces the following output:
php match_strict_comparison.php --- match (strict comparison) --- Result: Latent ability unreleased (strict match with integer 0) --- switch (loose comparison) --- Result: matched false (unintended loose comparison match)
match_unhandled_error.php
<?php
// Behavior when a match expression without default receives a value matching no arm
$characterName = "Broly";
$transformation = "Legendary Super Saiyan";
// Only expected transformations are listed as arms
// Without default, passing an unlisted value throws an exception
try {
$powerMultiplier = match($transformation) {
"Super Saiyan" => 50,
"Super Saiyan 2" => 100,
"Super Saiyan 3" => 400,
"Super Saiyan God" => 1000,
// "Legendary Super Saiyan" is not in the arms, so an exception is thrown
};
echo $characterName . ": multiplier " . $powerMultiplier . "x\n";
} catch (\UnhandledMatchError $e) {
// Thrown when a match expression without default receives a value that matches no arm
echo "UnhandledMatchError: transformation \"" . $transformation . "\" is not defined in the arms.\n";
}
// Adding default avoids the exception
$powerMultiplier2 = match($transformation) {
"Super Saiyan" => 50,
"Super Saiyan 2" => 100,
"Super Saiyan 3" => 400,
"Super Saiyan God" => 1000,
default => "immeasurable",
};
echo $characterName . ": multiplier " . $powerMultiplier2 . "\n";
Running the code produces the following output:
php match_unhandled_error.php UnhandledMatchError: transformation "Legendary Super Saiyan" is not defined in the arms. Broly: multiplier immeasurable
match_true_pattern.php
<?php
// match(true) can handle range comparisons
// Determining a Dragon Ball character's rank from their power level
function getBattleRank(int $power): string
{
// match(true) evaluates each arm's expression and returns the first that is true
return match(true) {
$power >= 1000000 => "Divine Realm",
$power >= 100000 => "Super Saiyan Class",
$power >= 10000 => "Elite Warrior Class",
$power >= 1000 => "Mid-Level Warrior Class",
default => "Common Warrior Class",
};
}
$fighters = [
["name" => "Goku", "power" => 3000000],
["name" => "Vegeta", "power" => 500000],
["name" => "Piccolo", "power" => 42000],
["name" => "Yamcha", "power" => 1480],
["name" => "Oolong", "power" => 10],
];
foreach ($fighters as $fighter) {
$rank = getBattleRank($fighter['power']);
echo $fighter['name'] . " (" . number_format($fighter['power']) . "): " . $rank . "\n";
}
Running the code produces the following output:
php match_true_pattern.php Goku (3,000,000): Divine Realm Vegeta (500,000): Super Saiyan Class Piccolo (42,000): Elite Warrior Class Yamcha (1,480): Mid-Level Warrior Class Oolong (10): Common Warrior Class
Common Mistakes
Common Mistake 1: UnhandledMatchError occurs due to missing default
When a 『match』 expression omits 『default』 and a value matching no arm is passed, an 『UnhandledMatchError』 exception is thrown. When unexpected values may be passed, either add 『default』 or catch the exception with 『try / catch』.
ng_no_default.php
<?php
$transformation = "Super Saiyan God"; // not in any arm
// Without default, passing an undefined value throws an exception
$multiplier = match($transformation) {
"Super Saiyan" => 50,
"Super Saiyan 2" => 100,
"Super Saiyan 3" => 400,
};
echo "Multiplier: " . $multiplier . "\n";
Running the code produces the following output:
php ng_no_default.php Fatal error: Uncaught UnhandledMatchError: Unhandled match case
ok_no_default.php
<?php
$transformation = "Super Saiyan God";
// Add default to handle undefined values
$multiplier = match($transformation) {
"Super Saiyan" => 50,
"Super Saiyan 2" => 100,
"Super Saiyan 3" => 400,
default => "immeasurable",
};
echo "Multiplier: " . $multiplier . "\n";
Running the code produces the following output:
php ok_no_default.php Multiplier: immeasurable
Common Mistake 2: Misplaced semicolon or missing comma between arms
Because 『match』 is an expression, it must be used as part of an assignment or another expression. The semicolon goes outside the closing brace of the match block. Each arm must be followed by a comma, including the last arm.
ng_semicolon.php
<?php
$rank = "Inspector";
// match is an expression, not a statement like switch
// Missing comma between arms causes a parse error
$description = match($rank) {
"Inspector" => "Public Safety Bureau Inspector"
// Missing comma — this causes a parse error
"Enforcer" => "Registered latent criminal"
};
Running the code produces the following output:
php ng_semicolon.php Parse error: syntax error, unexpected double-quoted string "Enforcer"
ok_semicolon.php
<?php
$rank = "Inspector";
// Each arm ends with a comma (including the last arm)
$description = match($rank) {
"Inspector" => "Public Safety Bureau Inspector",
"Enforcer" => "Registered latent criminal",
default => "Civilian",
};
echo $description . "\n";
Running the code produces the following output:
php ok_semicolon.php Public Safety Bureau Inspector
Common Mistake 3: Trying to use match like a switch statement with multi-statement blocks
Each arm of a 『match』 expression returns a single expression. It is not designed for executing multiple statements (e.g., echoing and then updating a variable). When multiple operations are needed, use 『switch』 or 『if』.
ng_multi_statement.php
<?php
$power = 9000;
// Each arm can only contain a single expression
// Trying to write a block with multiple statements causes a parse error
$result = match(true) {
$power >= 9000 => {
echo "Extremely high power level\n"; // Parse error: block syntax is not allowed
"Elite",
},
default => "Common",
};
Running the code produces the following output:
php ng_multi_statement.php
Parse error: syntax error, unexpected token "{"
ok_multi_statement.php
<?php
$power = 9000;
// Use if or switch when multiple operations are needed
if ($power >= 9000) {
echo "Extremely high power level\n";
$rank = "Elite";
} else {
$rank = "Common";
}
echo "Rank: " . $rank . "\n";
Running the code produces the following output:
php ok_multi_statement.php Extremely high power level Rank: Elite
Notes
The 『match』 expression was introduced in PHP 8 as a conditional branching syntax. Its key differences from 『switch』 are that it returns a value as an expression, and it uses strict comparison (===). Strict comparison prevents unintended matches caused by type coercion, such as integer 『0』 matching a string or 『false』. For details on type coercion, see 『Type Casting』.
Each arm can group multiple values with comma separation, allowing patterns previously handled by intentional fall-through in 『switch』 to be expressed more safely. Because 『break』 is not needed, bugs from forgetting it are also avoided. When 『default』 is omitted, a value that matches no arm causes an 『UnhandledMatchError』 exception. Omitting 『default』 is appropriate when you want unexpected values to be detected early as bugs; otherwise, adding 『default』 is the safer choice.
For range comparisons, 『match(true)』 can be used to evaluate each arm's condition expression in order. However, when conditions are complex or involve multiple variables, 『if / elseif / else』 may be more readable. For a detailed comparison with 『switch』, see 『switch statement』.
If you find any errors or copyright issues, please contact us.