Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Decisões com condicionais

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.

PHP Fundamentos Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
PHP / Fundamentos

Decisões com condicionais

por Beckett Hollis
Iniciante

Entenda 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.

↳ Pontos-chave
  • if, elseif e else sã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 um bool; use parênteses para deixar a precedência explícita.
  • 0, 0.0, "", "0", null e [] são avaliados como false numa condição; todo o resto é true.
  • O ternário cond ? a : b escolhe entre dois valores, e $x ?? "padrão" usa o padrão quando $x é null ou não existe.
  • match compara com ===, devolve um valor e lança UnhandledMatchError sem braço correspondente; switch compara com == e precisa de break.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01if, elseif e else no empréstimo da biblioteca

emprestimo.php
<?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

cupom.php
<?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

dado.php
<?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.

↳ Requisitos
  • Usar if, elseif e else para a classificação do tempo, sem match nem ternário.
  • Usar um segundo if/else para o rendimento das porções.
  • Guardar cada resultado em uma variável antes de imprimir.
  • Imprimir exatamente as linhas da saída esperada.
código inicial
<?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.
saída esperada
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

  1. As três variáveis descrevem a receita: nome, tempo em minutos e número de porções.
  2. O if ($minutos <= 30) testa a faixa mais curta primeiro; com 50 minutos, a condição é falsa.
  3. O elseif ($minutos <= 60) só é avaliado porque o teste anterior falhou, então ele já sabe que o tempo passa de 30.
  4. O else pega qualquer tempo acima de 60 minutos, sem precisar de condição.
  5. O segundo if/else é independente do primeiro e escolhe o texto do rendimento pelas porções.
  6. 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 vira média, porque essa condição já é verdadeira. Teste da faixa mais restrita para a mais ampla.
  • Usar if separados no lugar de elseif: if ($minutos <= 30) {...} if ($minutos <= 60) {...} sobrescreve rápida com média. Encadeie com elseif.
  • Escrever else if com condição no else: else ($minutos > 60) é erro de sintaxe. O else nã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.

↳ Requisitos
  • Criar calcularMulta(int $dias, string $leitor): float usando || para devolver zero sem atraso ou para professor.
  • Aplicar o teto de R$ 20,00 com um if, sem usar min().
  • Escolher entre o valor formatado e o texto isento com o operador ternário.
  • Imprimir exatamente as quatro linhas da saída esperada.
código inicial
<?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).
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
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

  1. As constantes guardam o valor por dia e o teto, para que os números não fiquem espalhados pelo código.
  2. O primeiro if junta com || os dois casos sem multa: sem atraso ou leitor professor. Basta um deles ser verdadeiro.
  3. O return 0.0 encerra a função ali mesmo, então o restante só roda para quem deve pagar.
  4. A multa é calculada multiplicando os dias pelo valor diário; com 30 dias, dá R$ 45,00.
  5. O segundo if corta o valor no teto: se passar de R$ 20,00, a multa vira exatamente o teto.
  6. Em descrever(), o ternário escolhe entre o valor formatado com number_format() e o texto isento.
  7. Cada chamada de descrever() produz uma linha, e o echo acrescenta PHP_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_MAXIMA mistura dias com reais e cobra errado entre 14 e 20 dias. Compare o valor da multa.
  • Imprimir o float direto: "R$ $multa" mostra R$ 6 em vez de R$ 6,00. Formate com number_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.

↳ Requisitos
  • Criar resumirPartida(?string $apelido, int $pontos, int $vidas, int $segundos): string com declare(strict_types=1).
  • Usar ?? para o apelido padrão e match (true) para o ranking, com default.
  • Tratar o fim de jogo com if e return antes 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.
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;
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
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

  1. O tipo ?string aceita uma string ou null, e o strict_types garante que os outros argumentos cheguem como int.
  2. $apelido ?? "Anônimo" usa o apelido quando ele existe e o nome padrão quando é null, sem precisar de if.
  3. O if ($vidas === 0) trata o fim de jogo primeiro e devolve a linha na hora; o ranking nem é calculado.
  4. O match (true) compara true com cada condição, em ordem, e devolve a letra do primeiro braço verdadeiro.
  5. Os limites vão do maior para o menor, e o default garante uma resposta para qualquer pontuação abaixo de 1000.
  6. O ternário do bônus exige as duas condições com &&: todas as vidas e menos de 120 segundos.
  7. Quando não há bônus, a variável fica com texto vazio e a interpolação no return nã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 >= 1000 no primeiro braço, 3200 pontos recebe B. No match (true), o primeiro braço verdadeiro vence; comece pelo maior limite.
  • Escrever match ($pontos) com condições nos braços: o PHP compara 3200 === true, nenhum braço bate e o resultado é UnhandledMatchError. Use match (true).
  • Esquecer o default: uma pontuação de 800 não satisfaz nenhum braço e lança UnhandledMatchError. Sempre cubra o resto com default.
  • Usar ?: no lugar de ??: $apelido ?: "Anônimo" também troca o texto vazio "" e o apelido "0", que são falsy. O ?? só substitui null.

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

Últimas lições

Ver o arquivo completo →