PHP 8.3.21 Released!

match

(PHP 8)

La expresión match permite realizar una evaluación basada en la verificación de identidad de un valor. De la misma manera que una instrucción switch, una expresión match tiene una expresión sujeto que se compara con varias alternativas. A diferencia de switch, se evalúa a un valor, como las expresiones ternarias. A diferencia de switch, la comparación es una verificación de identidad (===) en lugar de una verificación de igualdad débil (==). Las expresiones Match están disponibles a partir de PHP 8.0.0.

Ejemplo #1 Estructura de una expresión match

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

Ejemplo #2 Uso básico de las expresiones 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);
?>

El resultado del ejemplo sería:

string(19) "This food is a cake"

Ejemplo #3 Ejemplo de uso de match con operadores de comparación

<?php
$age
= 18;

$output = match (true) {
$age < 2 => "Bébé",
$age < 13 => "Enfant",
$age <= 19 => "Adolescent",
$age >= 40 => "Adulte âgé",
$age > 19 => "Jeune adulte",
};

var_dump($output);
?>

El resultado del ejemplo sería:

string(9) "Adolescent"

Nota: El resultado de una expresión match no necesita ser utilizado.

Nota: Cuando una expresión match se utiliza como una expresión autónoma, debe ser terminada por un punto y coma ;.

La expresión match es similar a una instrucción switch pero presenta algunas diferencias esenciales:

  • Una expresión match compara los valores de manera estricta (===) y no de manera débil como lo hace la instrucción switch.
  • Una expresión match devuelve un valor.
  • Las expresiones match no pasan a los casos siguientes como lo hacen las instrucciones switch.
  • Una expresión match debe ser exhaustiva.

Al igual que las instrucciones switch, las expresiones match se ejecutan brazo por brazo. Al principio, no se ejecuta ningún código. Las expresiones condicionales solo se evalúan si todas las expresiones condicionales anteriores no coinciden con la expresión del objeto. Solo la expresión de retorno correspondiente a la expresión condicional correspondiente será evaluada. Por ejemplo :

<?php
$result
= match ($x) {
foo() => 'value',
$this->bar() => 'value', // $this->bar() no es llamado si foo() === $x
$this->baz => beep(), // beep() no es llamado a menos que $x === $this->baz
// etc.
};
?>

Los brazos de la expresión de match pueden contener múltiples expresiones separadas por una coma. Se trata de un O lógico y una abreviatura para múltiples brazos que utilizan la misma expresión como resultado.

<?php
$result
= match ($x) {
// Este brazo
$a, $b, $c => 5,
// Es equivalente a estos tres brazos :
$a => 5,
$b => 5,
$c => 5,
};
?>

El patrón default es un caso especial. Este patrón coincide con todo lo que no ha sido buscado previamente. Por ejemplo :

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

Nota: El uso de múltiples patrones por defecto resultará en un error E_FATAL_ERROR.

Una expresión match debe ser exhaustiva. Si la expresión no es manejada por ningún brazo de match, un error UnhandledMatchError es lanzado.

Ejemplo #4 Ejemplo de una expresión match no manejada

<?php
$condition
= 5;

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

El resultado del ejemplo sería:

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
}

Uso de expresiones match para manejar los controles de no identidad

Es posible utilizar una expresión match para tratar los casos condicionales de no identidad utilizando true como expresión sujeto.

Ejemplo #5 Uso de una expresión match generalizada para realizar ramificaciones sobre rangos de enteros

<?php

$age
= 23;

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

var_dump($result);
?>

El resultado del ejemplo sería:

string(11) "young adult"

Ejemplo #6 Uso de una expresión match generalizada para realizar ramificaciones sobre el contenido de una cadena de caracteres

<?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);
?>

El resultado del ejemplo sería:

string(2) "fr"
add a note

User Contributed Notes 9 notes

up
93
darius dot restivan at gmail dot com
4 years ago
This will allow for a nicer FizzBuzz solution:

<?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);
}
up
76
Anonymous
3 years ago
<?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"),
};
}
?>

can be more concisely written as

<?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"),
};
}
?>
up
55
Hayley Watson
4 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($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"),
};
}
?>

Which also takes advantage of "throw" being handled as of PHP 8.0 as an expression instead of a statement.
up
10
tolga dot ulas at tolgaulas dot com
1 year ago
Yes it currently does not support code blocks but this hack works:

match ($foo){
'bar'=>(function(){
echo "bar";
})(),
default => (function(){
echo "baz";
})()
};
up
7
Sbastien
2 years ago
I use match instead of storing PDOStatement::rowCount() result and chaining if/elseif conditions or use the ugly 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
"Untouched rows : {$count_untouched}\r\n";
echo
"Inserted rows : {$count_inserted}\r\n";
echo
"Updated rows : {$count_updated}\r\n";
up
9
thomas at zuschneid dot de
2 years ago
While match allows chaining multiple conditions with ",", like:
<?php
$result
= match ($source) {
cond1, cond2 => val1,
default =>
val2
};
?>
it seems not valid to chain conditions with default, like:
<?php
$result
= match ($source) {
cond1 => val1,
cond2, default => val2
};
?>
up
13
php at joren dot dev
3 years ago
If you want to execute multiple return expressions when matching a conditional expression, you can do so by stating all return expressions inside an array.

<?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, )

?>
up
4
mark at manngo dot net
3 years ago
While you can’t polyfill a language construct, you can mimic the basic behaviour with a simple array.

Using example 2 above:

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

… you can get something similar with:

<?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;
?>
up
2
tm
3 years ago
If you are using a match expression for non-identity checks as described above make sure whatever you are using is actually returning `true` on success.

Quite often you rely on truthy vs. falsy when using if conditions and that will not work for match (for example `preg_match`). Casting to bool will solve this issue.
To Top