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

Você vai aprender
  • 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:

  1. A concatenação primeiro: "Você está " . $nota → "Você está 8".
  2. A comparação: "Você está 8" >= 7. Comparar texto com número dá um resultado sem sentido (aqui, true).
  3. 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".

O Elvis devolve o valor da condição

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çãoUse
Escolher entre dois valores curtosTernário: $x ? "sim" : "não"
Escolher dentro de um texto ou de um HTMLTerná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 maisif/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

Unparenthesized `a ? b : c ? d : e` is not supported

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.

unexpected token "echo" — comandos dentro do ternário
<?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.

unexpected token ";" — faltou o :
<?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.

Parte do texto sumiu

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

FormaLê-seExemplo → resultado
c ? a : bSe c, a; senão, b8 >= 7 ? "sim" : "não" → "sim"
a ?: ba, se for verdadeiro; senão, b"" ?: "padrão" → "padrão"
a ?? ba, se existir e não for null; senão, b"" ?? "padrão" → ""
c1 ? a : (c2 ? b : c)Aninhado, com parêntesesPermitido, mas prefira match
c1 ? a : c2 ? b : cAninhado sem parêntesesFatal error no PHP 8

Exercícios

1. Par ou ímpar

Complete para mostrar "par" ou "ímpar" (o % dá o resto da divisão):

echo $n % 2 === 0 ;

2. Valor padrão para vazio

Complete com o operador que troca um $apelido vazio por "sem apelido":

echo $apelido "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 aprovados
1 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 =, num echo, num texto.
  • No meio de uma concatenação, coloque o ternário entre parênteses.
  • $a ?: $b (Elvis): $a se for verdadeiro; senão $b. Trata "", "0" e 0 como vazios.
  • $a ?? $b: $a se existir e não for null; 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/elseif ou match.
  • O ternário escolhe valores; para executar comandos diferentes, use if.