Decisões com condicionais
por Beckett HollisEntenda o conceito
Condicionais permitem que o programa escolha um caminho conforme os dados. Em PHP, o if avalia uma condição entre parênteses e executa o bloco entre chaves quando ela é verdadeira; elseif testa outra condição se a anterior falhou, e else cobre todo o resto. O PHP testa de cima para baixo e executa só o primeiro bloco cuja condição for verdadeira.
As condições costumam ser comparações (===, !==, <, >=) combinadas com operadores lógicos: && exige que as duas partes sejam verdadeiras, || aceita qualquer uma e ! inverte o resultado. Qualquer valor pode servir de condição: 0, 0.0, "", "0", null e o array vazio contam como false, e o resto conta como true.
Para escolher um valor, há formas mais curtas: o ternário condição ? a : b e o operador ??, que usa um valor padrão quando a variável é null. Quando a decisão compara uma mesma expressão com vários valores, o match (PHP 8) é a opção moderna: compara com ===, devolve um valor e lança erro se nenhum braço servir. O switch, mais antigo, compara com == e exige break em cada caso.
if,elseifeelsesão testados em ordem, e só o primeiro bloco com condição verdadeira é executado; por isso as faixas vão da mais restrita para a mais ampla.&&exige as duas condições,||aceita qualquer uma e!inverte umbool; use parênteses para deixar a precedência explícita.0,0.0,"","0",nulle[]são avaliados comofalsenuma condição; todo o resto étrue.- O ternário
cond ? a : bescolhe entre dois valores, e$x ?? "padrão"usa o padrão quando$xénullou não existe. matchcompara com===, devolve um valor e lançaUnhandledMatchErrorsem braço correspondente;switchcompara com==e precisa debreak.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01if, elseif e else no empréstimo da biblioteca
<?php
// Situação de um empréstimo da biblioteca conforme os dias de atraso.
$livro = "Dom Casmurro";
$diasDeAtraso = 4;
// if/elseif/else: o PHP testa de cima para baixo e para no primeiro true.
if ($diasDeAtraso <= 0) {
$situacao = "em dia";
} elseif ($diasDeAtraso <= 7) {
$situacao = "atrasado";
} else {
$situacao = "bloqueado para novos empréstimos";
}
echo "$livro: $situacao" . PHP_EOL;
// Um if sozinho, sem else: só executa quando a condição é verdadeira.
if ($diasDeAtraso > 0) {
echo "Devolva o livro o quanto antes." . PHP_EOL;
}
// Comparações produzem bool e podem ser guardadas em variáveis.
$podeRenovar = $diasDeAtraso === 0;
if ($podeRenovar) {
echo "Renovação liberada." . PHP_EOL;
} else {
echo "Renovação indisponível enquanto houver atraso." . PHP_EOL;
}
A situação do empréstimo depende dos dias de atraso. O PHP testa $diasDeAtraso <= 0, que é falso, passa para $diasDeAtraso <= 7, que é verdadeiro, e executa só esse bloco; o else fica de fora. A ordem importa: se a faixa de até 7 dias viesse depois de uma condição mais ampla, nunca seria alcançada.
O segundo if não tem else: quando a condição é falsa, nada acontece. No fim, a comparação $diasDeAtraso === 0 é guardada em $podeRenovar, um bool com nome legível, e usada direto na condição. Dar nome a condições assim deixa o if fácil de ler.
02Operadores lógicos, ternário e ?? no cupom da loja
<?php
// Regras de um cupom de desconto da loja.
$totalCompra = 180.0;
$primeiraCompra = false;
$clienteVip = true;
$cupomDigitado = "";
// && exige as duas condições; || aceita qualquer uma delas.
if ($totalCompra >= 150 && ($primeiraCompra || $clienteVip)) {
$desconto = 0.10;
} else {
$desconto = 0.0;
}
// ! inverte um bool: aqui, "o frete NÃO é grátis".
$freteGratis = $totalCompra >= 200;
if (!$freteGratis) {
echo "Faltam R$ " . number_format(200 - $totalCompra, 2, ",", ".") . " para o frete grátis." . PHP_EOL;
}
// Ternário: escolhe entre dois valores numa expressão só.
$mensagem = $desconto > 0 ? "Desconto de 10% aplicado" : "Sem desconto";
echo $mensagem . PHP_EOL;
// Valores "falsy": "" (texto vazio), 0, "0", null e [] contam como false.
if ($cupomDigitado) {
echo "Cupom: $cupomDigitado" . PHP_EOL;
} else {
echo "Nenhum cupom informado." . PHP_EOL;
}
// ?? usa o valor da esquerda se ele existir e não for null.
$apelido = null;
echo "Obrigado, " . ($apelido ?? "cliente") . "!" . PHP_EOL;
$totalFinal = $totalCompra * (1 - $desconto);
echo "Total: R$ " . number_format($totalFinal, 2, ",", ".") . PHP_EOL;
A regra do desconto combina operadores: a compra precisa passar de R$ 150 e ser a primeira compra ou de um cliente VIP. Os parênteses em volta do || são essenciais, porque && tem precedência maior e, sem eles, a regra mudaria de sentido. O !$freteGratis inverte o bool para avisar quanto falta para o frete grátis.
O ternário escolhe a mensagem numa linha só, e o if ($cupomDigitado) mostra que um texto vazio conta como falso. Por fim, $apelido ?? "cliente" troca o null por um valor padrão sem precisar de um if inteiro.
03switch, match e match (true) no jogo de tabuleiro
<?php
// Resultado de um lançamento de dado num jogo de tabuleiro.
$dado = 6;
// switch compara com == e precisa de break para não "cair" no próximo case.
switch ($dado) {
case 1:
echo "Volte uma casa." . PHP_EOL;
break;
case 6:
echo "Jogue de novo!" . PHP_EOL;
break;
default:
echo "Ande $dado casas." . PHP_EOL;
}
// match compara com === e devolve um valor; vários valores por braço.
$efeito = match ($dado) {
1 => "azar",
2, 3, 4, 5 => "normal",
6 => "sorte",
};
echo "Efeito: $efeito" . PHP_EOL;
// match (true) testa condições em ordem: o primeiro braço true vence.
$pontos = 1450;
$medalha = match (true) {
$pontos >= 2000 => "ouro",
$pontos >= 1000 => "prata",
$pontos >= 500 => "bronze",
default => "nenhuma",
};
echo "Pontos: $pontos, medalha: $medalha" . PHP_EOL;
// Sem braço correspondente e sem default, match lança UnhandledMatchError.
try {
echo match ($dado + 1) {
1, 2, 3, 4, 5, 6 => "valor válido",
};
} catch (\UnhandledMatchError $erro) {
echo "Valor fora do dado: " . ($dado + 1) . PHP_EOL;
}
O switch compara o dado com cada case e executa a partir do primeiro que bate; o break impede que o PHP continue executando os casos de baixo. O match faz o mesmo trabalho de forma mais segura: compara com ===, aceita vários valores por braço (2, 3, 4, 5) e devolve o resultado direto para a variável.
Com match (true), cada braço é uma condição, e vence o primeiro que for verdadeiro; é uma alternativa compacta a uma sequência de elseif que só escolhe um valor. O último trecho mostra a proteção do match: o valor 7 não tem braço nem default, então o PHP lança UnhandledMatchError em vez de seguir com um resultado vazio.
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Tempo de preparo da receitaFácil
Classifique uma receita pelo tempo de preparo (até 30 minutos é rápida, até 60 é média, acima disso é demorada) e diga se as porções servem uma festa (8 ou mais) ou a família. Imprima as três linhas da saída esperada.
- Usar
if,elseifeelsepara a classificação do tempo, semmatchnem ternário. - Usar um segundo
if/elsepara o rendimento das porções. - Guardar cada resultado em uma variável antes de imprimir.
- Imprimir exatamente as linhas da saída esperada.
<?php
$receita = "Bolo de cenoura";
$minutos = 50;
$porcoes = 10;
// 1. Classifique o tempo de preparo.
// 2. Descubra para quantas pessoas a receita serve.
// 3. Imprima as três linhas.
Bolo de cenoura: 50 minutos
Classificação: média
Porções: 10 (serve uma festa)
Ver dica
Como o PHP para na primeira condição verdadeira, a faixa de 31 a 60 minutos não precisa testar se o tempo é maior que 30.
Ver resposta
<?php
$receita = "Bolo de cenoura";
$minutos = 50;
$porcoes = 10;
// Faixas de tempo testadas da menor para a maior.
if ($minutos <= 30) {
$classificacao = "rápida";
} elseif ($minutos <= 60) {
$classificacao = "média";
} else {
$classificacao = "demorada";
}
// Segunda decisão, independente da primeira.
if ($porcoes >= 8) {
$rendimento = "serve uma festa";
} else {
$rendimento = "serve a família";
}
echo "$receita: $minutos minutos" . PHP_EOL;
echo "Classificação: $classificacao" . PHP_EOL;
echo "Porções: $porcoes ($rendimento)" . PHP_EOL;
Passo a passo
- As três variáveis descrevem a receita: nome, tempo em minutos e número de porções.
- O
if ($minutos <= 30)testa a faixa mais curta primeiro; com 50 minutos, a condição é falsa. - O
elseif ($minutos <= 60)só é avaliado porque o teste anterior falhou, então ele já sabe que o tempo passa de 30. - O
elsepega qualquer tempo acima de 60 minutos, sem precisar de condição. - O segundo
if/elseé independente do primeiro e escolhe o texto do rendimento pelas porções. - As três linhas interpolam as variáveis no texto e terminam com
PHP_EOL.
Por que funciona
Uma cadeia if/elseif/else executa exatamente um dos blocos: o primeiro com condição verdadeira, ou o else se nenhuma for. Por isso as faixas podem ser escritas só com o limite superior: quando o PHP chega ao elseif, já é certo que o tempo passou de 30 minutos.
As duas decisões usam cadeias separadas porque respondem perguntas diferentes. Se o rendimento estivesse no mesmo if do tempo, uma receita rápida nunca teria as porções avaliadas.
Erros comuns
- Inverter a ordem das faixas: com
if ($minutos <= 60)primeiro, uma receita de 20 minutos viramédia, porque essa condição já é verdadeira. Teste da faixa mais restrita para a mais ampla. - Usar
ifseparados no lugar deelseif:if ($minutos <= 30) {...} if ($minutos <= 60) {...}sobrescreverápidacommédia. Encadeie comelseif. - Escrever
else ifcom condição noelse:else ($minutos > 60)é erro de sintaxe. Oelsenão recebe condição. - Usar
=no lugar de comparação:if ($porcoes = 8)atribui 8 e é sempre verdadeiro. Compare com>=ou===.
Outra forma de resolver
O ternário $rendimento = $porcoes >= 8 ? "serve uma festa" : "serve a família"; resolve a segunda decisão em uma linha. Ele é preferível quando a escolha tem só duas opções e cada uma é um valor simples; para três faixas, a cadeia de elseif continua mais legível.
Saída esperada
Bolo de cenoura: 50 minutos
Classificação: média
Porções: 10 (serve uma festa)
02Multa de atraso na bibliotecaMédio
Escreva uma função que calcule a multa de um empréstimo atrasado: R$ 1,50 por dia, com teto de R$ 20,00, e professores são isentos. Mostre o resultado de quatro leitores no formato da saída esperada.
- Criar
calcularMulta(int $dias, string $leitor): floatusando||para devolver zero sem atraso ou para professor. - Aplicar o teto de R$ 20,00 com um
if, sem usarmin(). - Escolher entre o valor formatado e o texto
isentocom o operador ternário. - Imprimir exatamente as quatro linhas da saída esperada.
<?php
declare(strict_types=1);
const MULTA_POR_DIA = 1.5;
const MULTA_MAXIMA = 20.0;
// Crie calcularMulta() e uma função que monte cada linha.
// Leitores: Ana (aluno, 4 dias), Bruno (aluno, 30 dias),
// Carla (professor, 12 dias) e Diego (aluno, 0 dias).
Ana (aluno, 4 dias): R$ 6,00
Bruno (aluno, 30 dias): R$ 20,00
Carla (professor, 12 dias): isento
Diego (aluno, 0 dias): isento
Ver dica
Trate primeiro os casos em que não há multa e devolva cedo com return; o cálculo e o teto ficam para depois, sem precisar de else.
Ver resposta
<?php
declare(strict_types=1);
const MULTA_POR_DIA = 1.5;
const MULTA_MAXIMA = 20.0;
// Calcula a multa de um empréstimo atrasado.
function calcularMulta(int $dias, string $leitor): float
{
// Sem atraso ou leitor professor: nada a pagar.
if ($dias <= 0 || $leitor === "professor") {
return 0.0;
}
$multa = $dias * MULTA_POR_DIA;
// O valor nunca passa do teto.
if ($multa > MULTA_MAXIMA) {
$multa = MULTA_MAXIMA;
}
return $multa;
}
// Monta a linha de saída com o valor no formato brasileiro.
function descrever(string $nome, int $dias, string $leitor): string
{
$multa = calcularMulta($dias, $leitor);
$valor = $multa > 0 ? "R$ " . number_format($multa, 2, ",", ".") : "isento";
return "$nome ($leitor, $dias dias): $valor";
}
echo descrever("Ana", 4, "aluno") . PHP_EOL;
echo descrever("Bruno", 30, "aluno") . PHP_EOL;
echo descrever("Carla", 12, "professor") . PHP_EOL;
echo descrever("Diego", 0, "aluno") . PHP_EOL;
Passo a passo
- As constantes guardam o valor por dia e o teto, para que os números não fiquem espalhados pelo código.
- O primeiro
ifjunta com||os dois casos sem multa: sem atraso ou leitor professor. Basta um deles ser verdadeiro. - O
return 0.0encerra a função ali mesmo, então o restante só roda para quem deve pagar. - A multa é calculada multiplicando os dias pelo valor diário; com 30 dias, dá R$ 45,00.
- O segundo
ifcorta o valor no teto: se passar de R$ 20,00, a multa vira exatamente o teto. - Em
descrever(), o ternário escolhe entre o valor formatado comnumber_format()e o textoisento. - Cada chamada de
descrever()produz uma linha, e oechoacrescentaPHP_EOL.
Por que funciona
A função resolve primeiro os casos especiais e devolve cedo, uma técnica conhecida como guard clause. Isso evita um if dentro de outro e deixa o caminho principal, o do cálculo, sem indentação extra. O || é avaliado em curto-circuito: se os dias já forem zero, o PHP nem compara o tipo de leitor.
O teto é uma segunda decisão, aplicada só depois do cálculo. Por fim, o ternário escolhe o texto exibido, separando a regra de negócio (quanto cobrar) da apresentação (como mostrar).
Erros comuns
- Trocar
||por&&:if ($dias <= 0 && $leitor === "professor")só isenta professores sem atraso, e Carla passaria a pagar R$ 18,00. A isenção vale para qualquer um dos casos. - Comparar o leitor com
=:$leitor = "professor"atribui o texto e a condição fica sempre verdadeira, isentando todos. Use===. - Aplicar o teto antes de multiplicar: comparar
$dias > MULTA_MAXIMAmistura dias com reais e cobra errado entre 14 e 20 dias. Compare o valor da multa. - Imprimir o float direto:
"R$ $multa"mostraR$ 6em vez deR$ 6,00. Formate comnumber_format($multa, 2, ",", ".").
Outra forma de resolver
return min($dias * MULTA_POR_DIA, MULTA_MAXIMA); aplica o teto numa linha, sem o segundo if. É preferível em código real quando o limite é só um número; o if explícito ganha quando o corte precisa fazer algo a mais, como registrar um aviso.
Saída esperada
Ana (aluno, 4 dias): R$ 6,00
Bruno (aluno, 30 dias): R$ 20,00
Carla (professor, 12 dias): isento
Diego (aluno, 0 dias): isento
03Ranking das partidas no placarDesafio
Escreva uma função que resuma a partida de um jogador: sem vidas é fim de jogo; caso contrário, defina o ranking pelos pontos (S a partir de 3000, A de 2000, B de 1000, senão C) e acrescente um bônus perfeito para quem terminou com 3 vidas em menos de 120 segundos. Jogadores sem apelido aparecem como Anônimo.
- Criar
resumirPartida(?string $apelido, int $pontos, int $vidas, int $segundos): stringcomdeclare(strict_types=1). - Usar
??para o apelido padrão ematch (true)para o ranking, comdefault. - Tratar o fim de jogo com
ifereturnantes do ranking, e decidir o bônus com&&em um ternário. - Reproduzir exatamente as cinco linhas da saída esperada com os dados do código inicial.
<?php
declare(strict_types=1);
// Crie resumirPartida() aqui.
echo resumirPartida("Raio", 3200, 3, 95) . PHP_EOL;
echo resumirPartida(null, 2100, 3, 150) . PHP_EOL;
echo resumirPartida("Luna", 1500, 1, 80) . PHP_EOL;
echo resumirPartida("Tito", 800, 2, 200) . PHP_EOL;
echo resumirPartida("Zeca", 2500, 0, 60) . PHP_EOL;
Raio: ranking S (3200 pontos) + bônus perfeito
Anônimo: ranking A (2100 pontos)
Luna: ranking B (1500 pontos)
Tito: ranking C (800 pontos)
Zeca: fim de jogo com 2500 pontos
Ver dica
No match (true), cada braço é uma comparação que vale true ou false; pense em qual ordem dos limites garante que 3200 pontos não caia no ranking A.
Ver resposta
<?php
declare(strict_types=1);
// Resume a partida de um jogador em uma linha.
function resumirPartida(?string $apelido, int $pontos, int $vidas, int $segundos): string
{
// ?? troca o apelido null por um nome padrão.
$nome = $apelido ?? "Anônimo";
// Sem vidas, a partida termina sem ranking.
if ($vidas === 0) {
return "$nome: fim de jogo com $pontos pontos";
}
// match (true): o primeiro braço verdadeiro define o ranking.
$ranking = match (true) {
$pontos >= 3000 => "S",
$pontos >= 2000 => "A",
$pontos >= 1000 => "B",
default => "C",
};
// Bônus só com todas as vidas E tempo abaixo de 2 minutos.
$bonus = $vidas === 3 && $segundos < 120 ? " + bônus perfeito" : "";
return "$nome: ranking $ranking ($pontos pontos)$bonus";
}
echo resumirPartida("Raio", 3200, 3, 95) . PHP_EOL;
echo resumirPartida(null, 2100, 3, 150) . PHP_EOL;
echo resumirPartida("Luna", 1500, 1, 80) . PHP_EOL;
echo resumirPartida("Tito", 800, 2, 200) . PHP_EOL;
echo resumirPartida("Zeca", 2500, 0, 60) . PHP_EOL;
Passo a passo
- O tipo
?stringaceita uma string ounull, e ostrict_typesgarante que os outros argumentos cheguem comoint. $apelido ?? "Anônimo"usa o apelido quando ele existe e o nome padrão quando énull, sem precisar deif.- O
if ($vidas === 0)trata o fim de jogo primeiro e devolve a linha na hora; o ranking nem é calculado. - O
match (true)comparatruecom cada condição, em ordem, e devolve a letra do primeiro braço verdadeiro. - Os limites vão do maior para o menor, e o
defaultgarante uma resposta para qualquer pontuação abaixo de 1000. - O ternário do bônus exige as duas condições com
&&: todas as vidas e menos de 120 segundos. - Quando não há bônus, a variável fica com texto vazio e a interpolação no
returnnão acrescenta nada.
Por que funciona
O match (true) funciona porque o match compara a expressão entre parênteses com cada braço usando ===. Como a expressão é true, vence o primeiro braço cuja condição também resulta em true. Isso substitui uma cadeia de elseif que só servia para escolher um valor, e o default impede o UnhandledMatchError.
A ordem das decisões segue a regra do enunciado: o fim de jogo tem prioridade e sai cedo com return, o ranking vem depois, e o bônus é uma condição composta avaliada à parte. Cada operador cuida de uma parte: ?? do valor ausente, match das faixas e && da regra que exige duas coisas ao mesmo tempo.
Erros comuns
- Colocar os limites em ordem crescente: com
$pontos >= 1000no primeiro braço, 3200 pontos recebeB. Nomatch (true), o primeiro braço verdadeiro vence; comece pelo maior limite. - Escrever
match ($pontos)com condições nos braços: o PHP compara3200 === true, nenhum braço bate e o resultado éUnhandledMatchError. Usematch (true). - Esquecer o
default: uma pontuação de 800 não satisfaz nenhum braço e lançaUnhandledMatchError. Sempre cubra o resto comdefault. - Usar
?:no lugar de??:$apelido ?: "Anônimo"também troca o texto vazio""e o apelido"0", que são falsy. O??só substituinull.
Outra forma de resolver
A mesma escolha de ranking pode ser feita com if/elseif/else atribuindo $ranking em cada bloco. Essa forma é preferível quando cada faixa precisa executar mais de uma instrução; quando o objetivo é só escolher um valor, o match (true) é mais curto e protege contra faixas esquecidas.
Saída esperada
Raio: ranking S (3200 pontos) + bônus perfeito
Anônimo: ranking A (2100 pontos)
Luna: ranking B (1500 pontos)
Tito: ranking C (800 pontos)
Zeca: fim de jogo com 2500 pontos