PHP match PHP 8.0+

O match chegou no PHP 8.0 para resolver os problemas do switch: nada de break esquecido, nada de comparação frouxa, e o resultado é um valor que você guarda direto numa variável. Para escolher um valor a partir de outro, é a ferramenta mais moderna e segura do PHP.

Você vai aprender
  • A anatomia do match: braços com =>, vírgulas e default.
  • Por que ele é uma expressão (devolve valor) e o switch não.
  • A comparação estrita (===): adeus às surpresas com "0", null e "1e1".
  • Vários valores no mesmo braço.
  • O UnhandledMatchError: o erro que aparece quando nenhum braço combina.
  • O match (true) para faixas de valores, e a tabela match × switch.

A ideia: uma tabela de consulta

Pense na tabela de frete pendurada na parede dos Correios: na coluna da esquerda, a região; na da direita, o preço. Você procura a sua linha e lê a resposta. O match ("combinar", "corresponder") é essa tabela escrita em PHP: cada linha tem um valor à esquerda e o resultado à direita, e o match devolve o resultado da linha que combinar exatamente.


A anatomia

<?php
$dia = 3;
$nome = match ($dia) {
    1 => "Domingo",
    2 => "Segunda",
    3 => "Terça",
    default => "Outro dia",
};
echo $nome, "\n";
$nome = match ($dia)
O valor examinado, entre parênteses. O match inteiro vale alguma coisa, e esse valor vai para $nome.
{ … }
Os braços (em inglês, arms), entre chaves.
1 => "Domingo",
Um braço: à esquerda da seta =>, o valor a comparar; à direita, o resultado. Termina com vírgula (não ponto e vírgula).
default => "Outro dia",
O braço para "nenhum dos anteriores". Opcional, mas veja o que acontece sem ele.
};
O ; depois da chave termina o comando de atribuição ($nome = …;). A vírgula do último braço é opcional, mas recomendada.
$ php dia.php
Terça

Compare com o mesmo programa em switch: aquele tinha 13 linhas, quatro break e um echo repetido em cada caso. O match tem um braço por linha e nenhum break: ele nunca cai no braço de baixo. Encontrou, devolveu, acabou.

A seta => é a mesma dos arrays associativos, e a semelhança é proposital: um match é quase um array de "chave → resultado", só que com mais poderes.

Uma expressão: pode ir em qualquer lugar

Como o ternário, o match é uma expressão. Ele pode ir direto num echo, num return, como argumento de função:

<?php
$dia = 7;
echo match ($dia) {
    1, 7 => "Fim de semana",
    default => "Dia útil",
}, "\n";
$ php direto.php
Fim de semana

Vários valores no mesmo braço

Na linha 1, 7 => "Fim de semana", os valores à esquerda estão separados por vírgula: "se for 1 ou 7". É o equivalente aos case empilhados do switch, numa linha só.

Missão: a lojinha da Ana cobra frete por estado: "SP", "RJ", "MG" e "ES" pagam "10,00"; "PE", "PB", "RN" e "CE" pagam "15,00"; os outros estados, "25,00". Complete o match para, com $uf = "PB", mostrar Frete: R$ 15,00.

<?php
$uf = "PB";
$frete = match ($uf) {
    "SP", "RJ", "MG", "ES" => "10,00",
    default => "25,00",
};
echo "Frete: R$ $frete";
<?php
$uf = "PB";
$frete = match ($uf) {
    "SP", "RJ", "MG", "ES" => "10,00",
    "PE", "PB", "RN", "CE" => "15,00",
    default => "25,00",
};
echo "Frete: R$ $frete";

Comparação estrita: ===

O match compara com ===: valor e tipo precisam ser iguais (veja Comparação (== e ===)). As surpresas da aula de switch desaparecem:

<?php
$opcao = "1";
echo match ($opcao) {
    1 => "número 1",
    "1" => "texto 1",
}, "\n";
$v = null;
echo match ($v) {
    0 => "zero",
    null => "nulo",
}, "\n";
$codigo = "1e1";
echo match ($codigo) {
    "10" => "Código 10",
    "1e1" => "Código 1e1",
}, "\n";
$ php estrito.php
texto 1          « "1" (string) não é 1 (int)
nulo             « null não é 0
Código 1e1       « "1e1" não é "10"

A contrapartida: dados de formulário chegam sempre como texto. Se $_GET['dia'] vale "3", o braço 3 => não combina. Converta antes, de propósito: match ((int) $_GET['dia']). Também não há meio-termo para maiúsculas: match ("Ana") não combina com "ana" =>; use mb_strtolower antes se for o caso (veja mbstring).


Nenhum braço combinou: UnhandledMatchError

O switch sem default simplesmente não faz nada quando nenhum caso combina. O match não aceita isso: como ele precisa devolver um valor, se nada combinar ele lança um erro.

<?php
$fruta = "kiwi";
echo match ($fruta) {
    "maçã", "pera" => "Fruta de árvore",
    "uva", "morango" => "Fruta pequena",
};
$ php kiwi.php

Fatal error: Uncaught UnhandledMatchError: Unhandled match case 'kiwi' in /home/ana/php/kiwi.php:3
Stack trace:
#0 {main}
  thrown in /home/ana/php/kiwi.php on line 3
UnhandledMatchError
O nome do erro: "erro de match não tratado".
Unhandled match case 'kiwi'
"Caso não tratado: 'kiwi'". O PHP mostra o valor que não encontrou braço. Com números, aparece sem aspas: Unhandled match case 5.
:3 / on line 3
A linha onde o match começa.
Uncaught
"Não capturado": dá para "capturar" esse erro com try/catch (veja Exceções). Sem isso, o programa para.

Isso é uma vantagem, não um defeito. Se um valor inesperado aparece (um estado novo, um código digitado errado), o switch esconde o problema; o match avisa na hora, apontando a linha e o valor. Quando "nenhum dos anteriores" é uma situação normal, coloque um default.


match (true): faixas de valores

Para faixas (nota maior que 9, maior que 7…), examine true e coloque condições à esquerda dos braços. O primeiro braço cuja condição for true (exatamente true, por causa do ===) é escolhido:

<?php
$nota = 8.5;
$conceito = match (true) {
    $nota >= 9 => "A",
    $nota >= 7 => "B",
    $nota >= 5 => "C",
    default => "D",
};
echo "Conceito $conceito\n";
$ php conceito.php
Conceito B

Os braços são testados de cima para baixo, como no elseif, então a ordem importa: comece pela faixa mais alta. É a forma mais enxuta de classificar valores em faixas, e substitui bem o switch (true) e as cadeias de elseif que só escolhem um valor.


O lado direito pode ser qualquer expressão

À direita da seta pode ir qualquer coisa que produza um valor: uma conta, uma concatenação, a chamada de uma função.

<?php
$acao = "sair";
match ($acao) {
    "entrar" => print("Bem-vinda!\n"),
    "sair" => print("Até logo!\n"),
};
$ php acoes.php
Até logo!

Aqui o match foi usado só pelo efeito (mostrar algo), e o valor devolvido foi ignorado. Funciona com print, que é uma expressão, mas não com echo, que é um comando (veja os erros abaixo). Se cada caso precisa executar várias linhas, use if ou switch: o lado direito do match é uma expressão só.


match × switch

match 8.0+switch
TipoExpressão: devolve um valorComando: executa linhas
Comparação=== (estrita)== (frouxa)
breakNão existe; nunca "cai" no próximoObrigatório para não cair (fall-through)
Vários valores1, 7 => …case 1: case 7:
Nada combinouUnhandledMatchError (se não houver default)Não faz nada, em silêncio
Cada casoUma expressãoQuantas linhas quiser
SeparadorVírgula depois de cada braço: depois do case, ; nos comandos
PHP antigoSó do 8.0 em dianteTodas as versões

Qual usar?

SituaçãoUse
Escolher um valor a partir de outromatch
Classificar em faixas, escolhendo um valormatch (true)
Cada caso executa várias linhas diferentesif/elseif ou switch
Dois caminhos sóif/else ou ternário
Código que precisa rodar em PHP 7switch ou if

Experimente: troque $nota por vários valores (10, 7, 4.9) e veja o conceito. Depois apague o braço default e rode com nota 2 para ver o UnhandledMatchError.

<?php
$nota = 6.5;
$conceito = match (true) {
    $nota >= 9 => "A",
    $nota >= 7 => "B",
    $nota >= 5 => "C",
    default => "D",
};
echo "Nota $nota: conceito $conceito\n";

Quando dá errado

UnhandledMatchError: Unhandled match case
<?php
$x = 5;
echo match ($x) {
    1 => "um",
    2 => "dois",
};
$ php cinco.php

Fatal error: Uncaught UnhandledMatchError: Unhandled match case 5 in /home/ana/php/cinco.php:3
Stack trace:
#0 {main}
  thrown in /home/ana/php/cinco.php on line 3

O que significa: nenhum braço combinou com o valor 5 e não há default.

Como consertar: se o valor é válido, acrescente o braço que falta. Se "qualquer outro" é normal, acrescente default => …. Se você esperava que combinasse, confira o tipo: "5" (texto) não combina com 5 =>.

unexpected integer "2", expecting "}" — faltou a vírgula
<?php
$x = 5;
echo match ($x) {
    1 => "um"
    2 => "dois",
};
$ php sem-virgula.php

Parse error: syntax error, unexpected integer "2", expecting "}" in /home/ana/php/sem-virgula.php on line 5

O que significa: sem a vírgula no fim da linha 4, o PHP achou que o match tinha acabado e esperava o }; encontrou o 2 da linha 5.

Como consertar: cada braço termina com vírgula. Quem vem do switch às vezes coloca ;, que também dá Parse error.

unexpected token "echo" — dois casos diferentes
<?php
$x = 1;
match ($x) {
    1 => echo "um",
};
$ php com-echo.php

Parse error: syntax error, unexpected token "echo" in /home/ana/php/com-echo.php on line 4

O que significa: à direita da seta só cabe uma expressão; echo é um comando. Coloque o echo por fora: echo match ($x) { 1 => "um", };.

O mesmo erro aparece quando falta o ; depois do } do match e a linha seguinte começa com echo:

<?php
$x = 1;
$r = match ($x) {
    1 => "um",
}
echo $r;
$ php sem-ponto.php

Parse error: syntax error, unexpected token "echo" in /home/ana/php/sem-ponto.php on line 6

Como consertar: };. O match é uma expressão dentro de um comando, e o comando precisa terminar com ;.

Match expressions may only contain one default arm
<?php
$x = 1;
$r = match ($x) {
    1 => "um",
    default => "outro",
    default => "de novo",
};
$ php dois-default.php

Fatal error: Match expressions may only contain one default arm in /home/ana/php/dois-default.php on line 6
Stack trace:
#0 {main}

Como consertar: um default só.


Referência

FormaExemplo
Básico$r = match ($x) { 1 => "um", 2 => "dois", };
Vários valores1, 7 => "fim de semana",
Padrãodefault => "outro",
Faixasmatch (true) { $n >= 7 => "B", … }
Direto no echoecho match ($x) { … };
Comparação===
Nada combinou, sem defaultUnhandledMatchError

Exercícios

1. A seta

Complete o braço com o símbolo que separa valor e resultado:

"pix" "Pagamento instantâneo",

2. Dois valores, um braço

Complete para "S" e "s" darem o mesmo resultado:

$resposta = match ($tecla) { => "sim", default => "não" };

3. Com $x = "2", o que match ($x) { 2 => "dois", default => "outro" } devolve?

4. O que acontece quando nenhum braço combina e não há default?

5. Qual destas afirmações é verdadeira?

Missão final: reescreva a classificação de estoque com match (true): 0 → esgotado; de 1 a 4 → poucas unidades; 5 ou mais → disponível. Com $estoque = 3, a saída deve ser Quantidade: poucas unidades.

<?php
$estoque = 3;
if ($estoque === 0) {
    $situacao = "esgotado";
} elseif ($estoque < 5) {
    $situacao = "poucas unidades";
} else {
    $situacao = "disponível";
}
echo "Quantidade: $situacao";
<?php
$estoque = 3;
$situacao = match (true) {
    $estoque === 0 => "esgotado",
    $estoque < 5 => "poucas unidades",
    default => "disponível",
};
echo "Quantidade: $situacao";

Resumo

  • match (PHP 8.0+) é uma expressão: $r = match ($x) { valor => resultado, … };.
  • Compara com ===: tipo e valor. Converta dados de formulário antes.
  • Não tem break nem fall-through: o primeiro braço que combinar devolve o valor.
  • Vários valores por braço: 1, 7 => …. Padrão: default => ….
  • Nada combinou e não há default? UnhandledMatchError, com o valor na mensagem.
  • match (true) classifica faixas de valores, de cima para baixo.
  • Para escolher valores, prefira match; para executar blocos de várias linhas, if ou switch.