PHP Operador Ternário
Muitas vezes um if serve só para escolher entre dois valores: "aprovado" ou "reprovado", "item" ou "itens". Para esses casos, o PHP tem um atalho que cabe numa linha: o operador ternário, escrito com ? e :.
- A anatomia do ternário:
condição ? valor_se_sim : valor_se_não. - A diferença entre um comando (
if) e uma expressão (o ternário), e por que isso importa. - Os parênteses que evitam surpresas quando o ternário está no meio de uma concatenação.
- O ternário curto
?:(o "Elvis") e a diferença para o??. - Por que ternário dentro de ternário sem parênteses é erro fatal no PHP 8.
A ideia: uma pergunta com duas respostas prontas
"Está frio? Se sim, casaco; se não, camiseta." A frase inteira resulta numa peça de roupa. O ternário é igual: uma pergunta seguida das duas respostas possíveis, e o resultado é um valor que você pode guardar, mostrar ou passar adiante.
O nome "ternário" quer dizer "de três partes": a condição, o valor do sim e o valor do não. É o único operador do PHP com três partes. (O +, por exemplo, tem duas: a + b.)
A anatomia
<?php $nota = 8; $situacao = $nota >= 7 ? "Aprovado" : "Reprovado"; echo $situacao, "\n";
- $nota >= 7
- A condição: a pergunta, igual à de um
if. - ?
- "Então…". Separa a pergunta das respostas.
- "Aprovado"
- O valor se a condição for verdadeira.
- :
- "Senão…".
- "Reprovado"
- O valor se a condição for falsa.
- $situacao = …
- O resultado (um dos dois valores) é guardado na variável.
$ php ternario.php Aprovado
O mesmo programa com if (veja if, elseif, else) teria cinco linhas:
<?php
$nota = 8;
if ($nota >= 7) {
$situacao = "Aprovado";
} else {
$situacao = "Reprovado";
}
echo $situacao, "\n";
$ php com-if.php Aprovado
Expressão × comando
Essa é a diferença importante. O if é um comando: ele escolhe quais linhas rodar. O ternário é uma expressão: ele vale alguma coisa, como 2 + 3 vale 5. Por isso o ternário pode ficar onde um valor pode ficar: depois de um =, dentro de um echo, como argumento de uma função, dentro de um texto concatenado.
<?php $qtd = 1; echo "$qtd " . ($qtd === 1 ? "item" : "itens") . " no carrinho\n"; $qtd = 3; echo "$qtd " . ($qtd === 1 ? "item" : "itens") . " no carrinho\n";
$ php plural.php 1 item no carrinho 3 itens no carrinho
O caso do plural é o exemplo clássico do ternário: com if, você precisaria de uma variável a mais.
Missão: usando um ternário, faça o programa dizer Bom dia, Ana! quando $hora for menor que 12 e Boa tarde, Ana! nos outros casos. Com $hora = 9, deve aparecer "Bom dia".
<?php $hora = 9; $saudacao = "Olá"; echo "$saudacao, Ana!";
<?php $hora = 9; $saudacao = $hora < 12 ? "Bom dia" : "Boa tarde"; echo "$saudacao, Ana!";
Use parênteses na concatenação
No exemplo do plural, o ternário estava entre parênteses. Veja o que acontece sem eles:
<?php $nota = 8; echo "Você está " . ($nota >= 7 ? "aprovado" : "reprovado") . "\n"; echo "Você está " . $nota >= 7 ? "aprovado" : "reprovado"; echo "\n";
$ php precedencia.php Você está aprovado aprovado « sumiu o "Você está "!
O ternário tem uma das menores prioridades entre os operadores (veja Precedência). Sem parênteses, o PHP fez, nesta ordem:
- A concatenação primeiro:
"Você está " . $nota→"Você está 8". - A comparação:
"Você está 8" >= 7. Comparar texto com número dá um resultado sem sentido (aqui,true). - Só então o ternário:
true ? "aprovado" : "reprovado"→"aprovado", e só isso foi mostrado.
Nenhum erro, nenhum aviso. A regra: ternário no meio de outra expressão, sempre entre parênteses.
O ternário curto ?: ("Elvis")
Muito comum é querer "o valor, se ele for verdadeiro; senão, um valor padrão". Com o ternário completo, você repete a variável: $nome ? $nome : "Visitante". O ternário curto omite o meio: $nome ?: "Visitante".
O apelido "operador Elvis" vem do desenho ?:, que lembra o topete e os olhos do cantor Elvis Presley, visto de lado.
<?php $nome = "Ana"; echo $nome ?: "Visitante", "\n"; $nome = ""; echo $nome ?: "Visitante", "\n"; $nome = "0"; echo $nome ?: "Visitante", "\n";
$ php elvis.php Ana « "Ana" é verdadeiro: fica o próprio valor Visitante « "" é falso: usa o padrão Visitante « "0" também é falso!
$a ?: $b quer dizer: "se $a for verdadeiro, $a; senão, $b". O "verdadeiro" aqui segue a tabela de valores falsos da aula de if: "", "0", 0, [], null e false fazem o Elvis usar o padrão.
?: × ??
O ?? (null coalescing, detalhado em ?? e ?->) parece igual, mas pergunta outra coisa: "o valor existe e não é null?".
<?php $nome = "0"; echo $nome ?: "Visitante", "\n"; echo $nome ?? "Visitante", "\n"; echo $semNome ?? "Visitante", "\n"; echo $semNome ?: "Visitante", "\n";
$ php elvis-ou-nulo.php Visitante « ?: acha "0" falso 0 « ?? só olha se é null: "0" não é, fica Visitante « ??: a variável não existe → padrão, sem aviso Warning: Undefined variable $semNome in /home/ana/php/elvis-ou-nulo.php on line 6 Visitante « ?: também usa o padrão, mas reclama da variável
Valor de $x | $x ?: "padrão" | $x ?? "padrão" |
|---|---|---|
"Ana" | "Ana" | "Ana" |
"" | "padrão" | "" |
"0" ou 0 | "padrão" | "0" ou 0 |
null | "padrão" | "padrão" |
| não existe | "padrão" + Warning | "padrão", sem aviso |
Qual usar: para dados que podem não existir (um campo de formulário, uma chave de array), ??. Para trocar valores vazios (um campo que veio mas em branco) por um padrão, ?:. Muitas vezes você quer os dois: ($_GET['nome'] ?? "") ?: "Visitante".
Com uma comparação na frente, o resultado do "sim" é o próprio true: $nota >= 7 ?: "não" vale true (que o echo mostra como 1) quando a nota é alta. O Elvis é para valores, não para perguntas.
Ternário dentro de ternário
A Ana quis classificar a nota em três faixas numa linha só:
<?php $nota = 6; echo $nota >= 7 ? "Aprovado" : $nota >= 5 ? "Recuperação" : "Reprovado";
$ php aninhado.php
Fatal error: Unparenthesized `a ? b : c ? d : e` is not supported. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)` in /home/ana/php/aninhado.php on line 3
Stack trace:
#0 {main}
"Ternário encadeado sem parênteses (a ? b : c ? d : e) não é suportado. Use (a ? b : c) ? d : e ou a ? b : (c ? d : e)." O próprio PHP mostra as duas formas possíveis. É um erro fatal, encontrado antes de rodar: nada do programa executa.
Por que o PHP proíbe? Por um motivo histórico: até o PHP 7, essa linha era aceita, mas o PHP a lia na ordem "errada" (da esquerda para a direita, diferente de todas as outras linguagens), o que gerava bugs. No PHP 7.4 virou aviso, e no PHP 8 virou erro. Com parênteses, a intenção fica clara:
<?php $nota = 6; echo $nota >= 7 ? "Aprovado" : ($nota >= 5 ? "Recuperação" : "Reprovado"), "\n";
$ php aninhado-certo.php Recuperação
Funciona, mas é difícil de ler. Para três ou mais caminhos, prefira if/elseif ou, ainda melhor, um match. Uma exceção: o ?: encadeado ($a ?: $b ?: "padrão") é permitido sem parênteses, porque ali não há ambiguidade.
Quando usar o ternário
| Situação | Use |
|---|---|
| Escolher entre dois valores curtos | Ternário: $x ? "sim" : "não" |
| Escolher dentro de um texto ou de um HTML | Ternário entre parênteses: "…" . ($x ? "a" : "b") . "…" |
| Valor padrão para algo vazio | ?: |
| Valor padrão para algo que pode não existir | ?? |
| Três caminhos ou mais | if/elseif ou match |
| Executar comandos diferentes (não escolher valores) | if/else |
Em templates HTML, o ternário é muito usado para atributos: <option <?= $uf === "PE" ? "selected" : "" ?>>.
Experimente: o ternário escolhe a classe e o texto do aviso de estoque. Troque $estoque para 0 e rode de novo.
<?php $estoque = 12; ?>
<h2>Uva — R$ 7,90/kg</h2>
<p style="color: <?= $estoque > 0 ? 'green' : 'red' ?>">
<?= $estoque > 0 ? "Disponível ($estoque kg)" : "Esgotado" ?>
</p>
Quando dá errado
O que significa: há um ternário dentro do "senão" de outro, sem parênteses. Veja Ternário dentro de ternário.
Como consertar: coloque parênteses no ternário de dentro, ou reescreva com if/elseif ou match.
<?php $nota = 8; $nota >= 7 ? echo "Aprovado" : echo "Reprovado";
$ php com-echo.php Parse error: syntax error, unexpected token "echo" in /home/ana/php/com-echo.php on line 3
O que significa: o ternário escolhe valores; echo é um comando, não um valor.
Como consertar: coloque o echo do lado de fora: echo $nota >= 7 ? "Aprovado" : "Reprovado";. Para executar comandos diferentes, use if.
:
<?php $nota = 8; $x = $nota >= 7 ? "sim";
$ php sem-senao.php Parse error: syntax error, unexpected token ";" in /home/ana/php/sem-senao.php on line 3
O que significa: o ternário completo precisa das três partes. O PHP esperava o : e o valor do "senão", e encontrou o fim do comando.
Como consertar: $x = $nota >= 7 ? "sim" : "não";. Se você queria "só quando for verdade", é um if.
O que significa: um ternário sem parênteses no meio de uma concatenação. Veja Use parênteses na concatenação.
Como consertar: "texto " . ($cond ? "a" : "b") . " texto".
Referência
| Forma | Lê-se | Exemplo → resultado |
|---|---|---|
c ? a : b | Se c, a; senão, b | 8 >= 7 ? "sim" : "não" → "sim" |
a ?: b | a, se for verdadeiro; senão, b | "" ?: "padrão" → "padrão" |
a ?? b | a, se existir e não for null; senão, b | "" ?? "padrão" → "" |
c1 ? a : (c2 ? b : c) | Aninhado, com parênteses | Permitido, mas prefira match |
c1 ? a : c2 ? b : c | Aninhado sem parênteses | Fatal error no PHP 8 |
Exercícios
1. Par ou ímpar
Complete para mostrar "par" ou "ímpar" (o % dá o resto da divisão):
2. Valor padrão para vazio
Complete com o operador que troca um $apelido vazio por "sem apelido":
3. Com $x = 0;, o que mostra echo $x ?? 10;?
4. Com $x = 0;, o que mostra echo $x ?: 10;?
5. O que acontece no PHP 8 com echo $a ? "x" : $b ? "y" : "z";?
Missão final: o resumo da turma está com o plural errado. Use um ternário em cada linha para escolher entre "aluno"/"alunos" e "aprovado"/"aprovados" (e "reprovado"/"reprovados"). A saída deve ser:2 alunos aprovados1 aluno reprovado
<?php $aprovados = 2; $reprovados = 1; echo "$aprovados alunos aprovados\n"; echo "$reprovados alunos reprovados\n";
<?php $aprovados = 2; $reprovados = 1; echo "$aprovados " . ($aprovados === 1 ? "aluno aprovado" : "alunos aprovados") . "\n"; echo "$reprovados " . ($reprovados === 1 ? "aluno reprovado" : "alunos reprovados") . "\n";
Resumo
condição ? sim : nãoé uma expressão: ela vale um dos dois valores e pode ir num=, numecho, num texto.- No meio de uma concatenação, coloque o ternário entre parênteses.
$a ?: $b(Elvis):$ase for verdadeiro; senão$b. Trata"","0"e0como vazios.$a ?? $b:$ase existir e não fornull; não avisa sobre variáveis inexistentes.- Ternário dentro de ternário sem parênteses é erro fatal no PHP 8. Para três caminhos ou mais, use
if/elseifoumatch. - O ternário escolhe valores; para executar comandos diferentes, use
if.