match

(PHP 8)

A expressão match ramifica a avaliação baseada em uma verificação de identidade de um valor. Semelhante a uma declaração switch, uma expressão match possui uma expressão sujeito que é comparada com múltiplas alternativas. Ao contrário da switch, ela irá avaliar para um valor assim como as expressões ternárias. Diferente da switch, a comparação é uma verificação de identidade (===) em vez de uma comparação de equalidade fraca (==). Expressões match estão diponíveis a partir do PHP 8.0.0.

Exemplo #1 Estrutura de uma expressão match

<?php
$valor_de_retorno
= match (expressao_sujeito) {
expressao_condicional_unica => expressao_de_retorno,
expressao_condicional1, expressao_condicional2 => expressao_de_retorno,
};
?>

Exemplo #2 Basic match usage

<?php
$comida
= 'bolo';

$valor_de_retorno = match ($comida) {
'apple' => 'Essa comida é uma maçã',
'bar' => 'Essa comida é um bar',
'bolo' => 'Essa comida é um bolo',
};

var_dump($valor_de_retorno);
?>

O exemplo acima produzirá:

string(19) "Essa comida é um bolo"

Nota: O resultado de uma expressão match não precisa ser usado.

Nota: Uma expressão match deve ser terminada por um ponto e vírgula ;.

A expressão match é similar a uma declaração switch mas tem algumas diferenças importantes:

  • Um braço match compara valores estritamente (===) em vez de frouxamente como a declaração switch faz.
  • Uma expressão match retorna um valor.
  • Braços match não seguem para casos posteriores da maneira que declarações switch seguem.
  • Uma expressão match deve ser exaustiva.

Assim como as declarações switch, expressões match são executadas braço de correspondência por braço de correspondência. No início, nenhum código é executado. A expressões condicionais só são avaliadas se todas as expressões condicionais anteriores não corresponderem à expressão sujeito. Apenas a expressão de retorno pertencente à expressão condicional de correspondência será avaliada.

<?php
$resultado
= match ($x) {
foo() => ...,
$this->bar() => ..., // $this->bar() não é chamado se foo() === $x
$this->baz => beep(), // beep() não é chamado a não ser que $x === $this->baz
// etc.
};
?>

Braços da expressão match podem conter múltiplas expressões separadas por uma vírgula. Isso é um OR lógico, e é uma forma abreviada para múltimplos braços de correspondência com o mesmo lado direito.

<?php
$resultado
= match ($x) {
// Este braço de correspondência:
$a, $b, $c => 5,
// É equivalente a estes três braços de correspondência:
$a => 5,
$b => 5,
$c => 5,
};
?>

Um caso especial é o padrão default. Esse padrão atende a qualquer coisa que não tenha sido atendida anteriormente. Por exemplo:

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

Nota: Múltiplos padrões default irão emitir um erro E_FATAL_ERROR.

Uma expressão match deve ser exaustiva. Se a expressão sujeito não for tratada por nenhum braço de correspondência um UnhandledMatchError é lançado.

Exemplo #3 Exemplo de uma expressão match não tratada

<?php
$condicao
= 5;

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

O exemplo acima produzirá:

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
}

Usando expressões match para tratar verificações sem identidade

É possível usar uma expressão match para tratar casos condicionais sem identidade usando true como a expressão sujeito.

Exemplo #4 Usando uma expressão match generalizada para ramificar com base em intervalos de inteiros

<?php

$idade
= 23;

$resultado = match (true) {
$idade >= 65 => 'senhor',
$idade >= 25 => 'adulto',
$idade >= 18 => 'adolescente',
default =>
'criança',
};

var_dump($resultado);
?>

O exemplo acima produzirá:

string(11) "adolescente"

Exemplo #5 Usando uma expressão match generalizada para ramificar com base no conteúdo de strings

<?php

$texto
= 'Bienvenue chez nous';

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

var_dump($resultado);
?>

O exemplo acima produzirá:

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

User Contributed Notes 8 notes

up
72
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
61
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
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($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
5
Sbastien
1 year 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
6
thomas at zuschneid dot de
1 year 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
8
php at joren dot dev
2 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
5
mark at manngo dot net
2 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
0
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