PHP switch

Quando você compara a mesma variável com uma lista de valores (o dia da semana é 1? é 2? é 3?…), um if/elseif fica longo e repetitivo. O switch foi feito para isso: você diz uma vez qual valor está examinando e lista os casos.

Você vai aprender
  • A anatomia do switch: case, break e default.
  • O que acontece quando falta um break (o fall-through) e como usar isso a seu favor.
  • Vários valores levando ao mesmo bloco.
  • A comparação frouxa (==) do switch e as surpresas com "0", null e "1e1".
  • O truque do switch (true), a sintaxe endswitch e os erros reais.

A ideia: a central telefônica

"Para falar com o financeiro, digite 1. Para o suporte, digite 2. Para outros assuntos, aguarde." A central olha o número que você digitou e pula direto para o atendimento certo. O switch ("chave", "interruptor" de desvio) funciona assim: olha um valor e salta para o case ("caso") correspondente.


A anatomia

<?php
$dia = 3;
switch ($dia) {
    case 1:
        echo "Domingo\n";
        break;
    case 2:
        echo "Segunda\n";
        break;
    case 3:
        echo "Terça\n";
        break;
    default:
        echo "Outro dia\n";
}
switch ($dia)
O valor examinado, entre parênteses. É calculado uma vez.
{ … }
Todos os casos ficam dentro de um par de chaves.
case 1:
"No caso de ser 1". Um valor seguido de dois-pontos. O PHP compara $dia == 1.
echo …
As linhas que rodam a partir desse caso. Não precisam de chaves.
break;
"Interromper": sai do switch. Sem ele, o PHP continua rodando os casos de baixo (já, já você vê).
default:
"Padrão": roda quando nenhum case combinou. É opcional, como o else. Por costume, fica no fim.
$ php dia.php
Terça

Como o PHP faz: compara $dia com o case 1 (não), com o case 2 (não), com o case 3 (sim!) e começa a rodar dali, até encontrar o break. O mesmo programa com if seria if ($dia == 1) … elseif ($dia == 2) … elseif ($dia == 3) … else …, repetindo $dia == em cada linha.


Sem break: o fall-through

O case é só um ponto de entrada, como uma porta. Depois de entrar, o PHP segue em frente, linha por linha, passando por cima dos outros case, até achar um break ou o fim do switch. Veja o que acontece quando a Ana esquece todos os break:

<?php
$dia = 2;
switch ($dia) {
    case 1:
        echo "Domingo\n";
    case 2:
        echo "Segunda\n";
    case 3:
        echo "Terça\n";
    default:
        echo "Outro dia\n";
}
$ php sem-break.php
Segunda          « entrou no case 2...
Terça            « ...e "caiu" no case 3...
Outro dia        « ...e no default

Esse comportamento se chama fall-through ("cair através"): a execução cai de um caso para o próximo. Nenhum erro ou aviso: o PHP acha que você quis assim. Esquecer um break é o bug mais comum com switch.

Regra prática

Termine todo case com break. Se um dia você quiser cair de propósito no caso de baixo, deixe um comentário dizendo isso (// continua no próximo caso), para ninguém "consertar" depois.

O lado bom: vários valores, um bloco

O fall-through tem um uso legítimo e muito comum: vários case vazios, um embaixo do outro, levando ao mesmo bloco.

<?php
$fruta = "uva";
switch ($fruta) {
    case "maçã":
    case "pera":
        echo "Fruta de árvore\n";
        break;
    case "uva":
    case "morango":
        echo "Fruta pequena\n";
        break;
    default:
        echo "Não sei\n";
}
$ php frutas.php
Fruta pequena

O case "uva": não tem nada; a execução "cai" no case "morango":, que também não tem nada, e chega ao echo. Leia como "no caso de uva ou morango".

Missão: com $dia = 7 (sábado), o programa deve mostrar Fim de semana. Os dias 1 (domingo) e 7 (sábado) são fim de semana; os outros, dia útil. Junte case 1: e case 7: no mesmo bloco.

<?php
$dia = 7;
switch ($dia) {
    case 1:
        echo "Fim de semana";
        break;
    default:
        echo "Dia útil";
}
<?php
$dia = 7;
switch ($dia) {
    case 1:
    case 7:
        echo "Fim de semana";
        break;
    default:
        echo "Dia útil";
}

A comparação frouxa: switch usa ==

Aqui está a maior armadilha. O switch compara com == (igualdade "frouxa", que converte os tipos), e não com === (veja Comparação (== e ===)). Na maior parte do tempo isso não aparece. Mas veja este switch, rodado com vários valores de $v:

<?php
$v = null;     // troque por "0", 0, "", false, "a" e rode de novo
switch ($v) {
    case 0:
        echo "case 0\n";
        break;
    case "":
        echo "case \"\"\n";
        break;
    default:
        echo "default\n";
}
Valor de $vSaída realPor quê
"0"case 0"0" == 0: o texto numérico vira número
0case 0Igual
""case ""No PHP 8, "" == 0 é false
nullcase 0null == 0 é true: os dois viram "falso"
falsecase 0false == 0 é true
"a"defaultNenhum caso combina
$ php frouxo.php
case 0              « com $v = null: uma variável "sem valor" entrou no caso do zero

Se $v fosse uma quantidade que não veio do formulário (null), o programa trataria como "zero itens", sem aviso nenhum. E tem um caso ainda mais estranho, com textos que parecem números:

<?php
$codigo = "1e1";
switch ($codigo) {
    case "10":
        echo "Código 10?!\n";
        break;
    default:
        echo "outro\n";
}
$ php codigo.php
Código 10?!

"1e1" é a notação científica de 10 (1 × 10¹). Quando os dois lados do == são textos numéricos, o PHP compara como números, e 10 == 10. Se esses fossem códigos de produto da lojinha, o produto "1e1" seria confundido com o "10".

Como se proteger
  • Garanta o tipo antes do switch: switch ((int) $dia) para números, ou switch ((string) $codigo)… (mas lembre: dois textos numéricos ainda se comparam como números).
  • Use match PHP 8.0+, que compara com ===. É a próxima aula, e é o motivo principal de o match existir.

switch (true): faixas de valores

O case aceita qualquer expressão, não só valores fixos. Um truque comum é examinar true e colocar condições nos case: o primeiro cuja condição der true é escolhido.

<?php
$nota = 8;
switch (true) {
    case $nota >= 7:
        echo "Aprovado\n";
        break;
    case $nota >= 5:
        echo "Recuperação\n";
        break;
    default:
        echo "Reprovado\n";
}
$ php faixas.php
Aprovado

Funciona, mas a comparação frouxa continua valendo (true == "texto" também é verdadeiro), e o if/elseif faz o mesmo de forma mais direta. Você vai ver esse truque em códigos existentes; para os seus, prefira if/elseif ou match (true).


Detalhes que você vai encontrar

Sintaxe alternativa: endswitch

Como o if, o switch tem uma forma com : e endswitch; para templates HTML (veja PHP Dentro do HTML):

<?php
$x = 1;
switch ($x):
    case 1:
        echo "um\n";
        break;
endswitch;
$ php alternativa.php
um

Na prática, é raro: no meio do HTML, um switch fica confuso. Calcule o valor antes, em PHP puro, e mostre só o resultado.

Nenhum caso e nenhum default

Se nada combinar e não houver default, o switch simplesmente não faz nada, sem aviso. (O match, ao contrário, dá erro; veja a próxima aula.)

return no lugar do break

Dentro de uma função, é comum cada case terminar com return valor;: o return sai da função inteira, então o break não é necessário.


Quando dá errado

Rodou mais de um caso
$ php sem-break.php
Segunda
Terça
Outro dia

O que significa: faltou break no fim de um case, e a execução caiu nos de baixo.

Como consertar: coloque break; como última linha de cada case que tem código.

Switch statements may only contain one default clause
<?php
$x = 1;
switch ($x) {
    case 1:
        echo "um\n";
        break;
    default:
        echo "outro\n";
        break;
    default:
        echo "de novo\n";
}
$ php dois-default.php

Fatal error: Switch statements may only contain one default clause in /home/ana/php/dois-default.php on line 10
Stack trace:
#0 {main}

O que significa: "um switch só pode ter um default". Acontece ao copiar e colar blocos. É detectado antes de rodar: nada executa.

Como consertar: deixe um só default.

unexpected token "echo" — faltou o : do case
<?php
$x = 1;
switch ($x) {
    case 1:
        echo "um\n";
        break;
    case 2
        echo "dois\n";
}
$ php sem-dois-pontos.php

Parse error: syntax error, unexpected token "echo" in /home/ana/php/sem-dois-pontos.php on line 8

O que significa: depois do valor do case, o PHP esperava :. Ele só percebe o problema na linha seguinte (8), ao encontrar o echo.

Como consertar: case 2:. (Curiosidade: case 2; com ponto e vírgula é aceito, por motivos históricos. Não use.)

"continue" targeting switch is equivalent to "break"
<?php
$x = 1;
switch ($x) {
    case 1:
        echo "a\n";
        continue;
}
$ php continue.php

Warning: "continue" targeting switch is equivalent to "break" in /home/ana/php/continue.php on line 6
a

O que significa: "um continue que mira o switch equivale a break". Para o PHP, o switch conta como uma estrutura de repetição para o break e o continue. Isso confunde quando o switch está dentro de um laço e você queria pular para a próxima volta do laço.

Como consertar: use break para sair do switch. Para pular a volta do laço de fora, use continue 2 (veja break e continue).

Entrou no caso errado, sem nenhum erro
$ php codigo.php
Código 10?!

O que significa: a comparação frouxa (==) achou iguais valores que você considera diferentes: null e 0, "1e1" e "10", false e 0.

Como consertar: converta o valor para o tipo certo antes do switch, ou troque por match, que usa ===.


Referência

ParteO que faz
switch (valor) { … }Examina o valor uma vez
case X:Ponto de entrada quando valor == X
case A: case B:A ou B levam ao mesmo bloco
break;Sai do switch
default:Nenhum caso combinou (no máximo um)
switch (true) { case $n > 5: … }Faixas de valores
switch (…): … endswitch;Sintaxe alternativa
Comparação== (frouxa)

Exercícios

1. Sair do caso

Complete a palavra que impede a execução de cair no próximo case:

case "pix": echo "Pagamento instantâneo";

2. Nenhum caso

Complete com a palavra do caso "padrão":

echo "Forma de pagamento desconhecida";

3. Com $n = 1, o que mostra switch ($n) { case 1: echo "A"; case 2: echo "B"; break; case 3: echo "C"; }?

4. Com $v = null, em qual caso o switch ($v) entra, se houver case 0: e default:?

5. Que tipo de comparação o switch usa?

Missão final: na lojinha, o pagamento "pix" deve mostrar Pagamento por PIX e, na linha de baixo, Desconto de 5%; "cartao" mostra Pagamento no cartão; qualquer outro, Forma desconhecida. O código abaixo tem um bug: rode com $pagamento = "pix" e veja o que sobra na saída. Corrija.

<?php
$pagamento = "pix";
switch ($pagamento) {
    case "pix":
        echo "Pagamento por PIX\n";
        echo "Desconto de 5%\n";
    case "cartao":
        echo "Pagamento no cartão\n";
        break;
    default:
        echo "Forma desconhecida\n";
}
<?php
$pagamento = "pix";
switch ($pagamento) {
    case "pix":
        echo "Pagamento por PIX\n";
        echo "Desconto de 5%\n";
        break;
    case "cartao":
        echo "Pagamento no cartão\n";
        break;
    default:
        echo "Forma desconhecida\n";
}

Resumo

  • switch examina um valor e salta para o case igual; default é o "nenhum dos anteriores".
  • Cada case é só uma porta de entrada: sem break, a execução cai nos casos de baixo (fall-through).
  • cases vazios empilhados = "este ou aquele".
  • O switch compara com ==: null e false entram no case 0, "1e1" entra no case "10".
  • Para comparação estrita e código mais curto, o PHP 8 tem o match: a próxima aula.