PHP ?? e ?->
Muitas vezes um valor pode estar faltando: o cliente não informou o telefone, o endereço não tem complemento, a página foi aberta sem o nome na URL. O operador ?? resolve isso numa linha: "use este valor; se ele não existir ou for null, use aquele outro". Nesta aula você vai conhecer o ??, o seu irmão ??= e o ?->, que evita um erro fatal quando o objeto é null.
- O que o
??faz e por que ele não mostraWarningquando a variável não existe. - A diferença entre
??,isset, o ternário e o?:(com0e""isso importa muito). - Como encadear vários
??e como usar o??=para preencher só o que está faltando. - O operador nullsafe
?->PHP 8.0+ e o erroCall to a member function … on null. - A ideia do
$_GET["nome"] ?? "visitante", que você vai usar em todo formulário.
Antes, é bom ter visto null (o valor "nada") e Booleanos. Alguns exemplos usam um array com nomes nas posições, como $aluna["nome"]; basta saber que é uma "ficha" com campos, e a aula Arrays Associativos explica tudo.
A ideia: o plano B
Pense numa ficha de cadastro em papel. Quando a Ana vai ler o campo "Apelido" e ele está em branco, ela usa o nome. Quando o campo "Telefone" está em branco, ela escreve "não informado". Ninguém para o trabalho por causa de um campo vazio: existe um plano B.
O ?? é exatamente isso. Ele olha o valor da esquerda:
- se ele existe e não é
null, usa ele; - senão, usa o valor da direita (o plano B, ou "valor padrão").
O nome oficial é null coalescing operator, "operador de coalescência nula". Coalescer quer dizer "juntar": ele junta vários candidatos e fica com o primeiro que não é null.
O problema: o que não existe gera um aviso
Primeiro, veja o que acontece sem o ?? quando a variável não existe:
<?php echo $sobrenome, "\n"; echo "fim\n";
$ php sem.php Warning: Undefined variable $sobrenome in /home/ana/site/sem.php on line 2 fim
O PHP avisa "variável indefinida: $sobrenome" (veja Lendo Erros do PHP), trata o valor como null e continua. Com uma chave de array que não existe, acontece o mesmo:
<?php $aluna = ["nome" => "Ana", "nota" => 8]; echo $aluna["nome"], "\n"; echo $aluna["email"], "\n"; echo $aluna["email"] ?? "sem e-mail", "\n";
$ php aluno.php Ana Warning: Undefined array key "email" in /home/ana/site/aluno.php on line 5 sem e-mail
A linha 5 deu o aviso Undefined array key "email" ("chave de array indefinida") e mostrou uma linha vazia. A linha 6 é a mesma coisa com o ??: nada de aviso, e o plano B apareceu.
O operador ??
<?php $apelido = null; $nome = "Ana"; echo $apelido ?? "sem apelido", "\n"; echo $nome ?? "sem nome", "\n"; echo $sobrenome ?? "sem sobrenome", "\n";
$ php coalescencia.php sem apelido Ana sem sobrenome
- $apelido
- O valor que você quer usar (o plano A).
- ??
- "Se o da esquerda não existir ou for
null…" - "sem apelido"
- "…use este" (o plano B). Pode ser um texto, um número, outra variável, o resultado de uma conta.
Linha por linha:
| Expressão | O valor da esquerda | Resultado |
|---|---|---|
$apelido ?? "sem apelido" | existe, mas é null | "sem apelido" |
$nome ?? "sem nome" | existe e vale "Ana" | "Ana" |
$sobrenome ?? "sem sobrenome" | não existe | "sem sobrenome", sem Warning |
Esse "sem Warning" é o grande trunfo do ??. Ele verifica se a variável existe antes de ler o valor. Funciona até com várias chaves seguidas: se o pedido não tiver cliente, $pedido["cliente"]["email"] ?? "sem e-mail" dá "sem e-mail", sem nenhum aviso.
Missão: rode e veja o Warning da última linha. Depois use o ?? para mostrar Telefone: não informado, sem aviso nenhum. Copie o jeito da linha do e-mail (os parênteses são necessários; a próxima seção explica).
<?php $aluna = ["nome" => "Ana", "email" => "ana@exemplo.com"]; echo "Nome: " . $aluna["nome"] . "\n"; echo "E-mail: " . ($aluna["email"] ?? "não informado") . "\n"; echo "Telefone: " . $aluna["telefone"] . "\n";
<?php $aluna = ["nome" => "Ana", "email" => "ana@exemplo.com"]; echo "Nome: " . $aluna["nome"] . "\n"; echo "E-mail: " . ($aluna["email"] ?? "não informado") . "\n"; echo "Telefone: " . ($aluna["telefone"] ?? "não informado") . "\n";
Por que os parênteses com o .?
O ?? tem prioridade menor que o ponto que junta textos. Sem parênteses, "Telefone: " . $aluna["telefone"] ?? "não informado" é lido como ("Telefone: " . $aluna["telefone"]) ?? "não informado": o PHP junta os textos primeiro (com o Warning) e o resultado, um texto, nunca é null. Então, sempre que o ?? estiver no meio de outros operadores, coloque-o entre parênteses. A aula Precedência mostra esse caso com a saída real.
Encadeando: plano B, plano C…
Dá para colocar vários ?? seguidos. O PHP fica com o primeiro valor que existe e não é null:
<?php $apelido = null; $nome = null; echo $apelido ?? $nome ?? "visitante", "\n"; $nome = "Ana"; echo $apelido ?? $nome ?? "visitante", "\n";
$ php cadeia.php visitante Ana
Na primeira vez, o apelido e o nome são null, então sobra "visitante". Na segunda, o apelido continua null, mas o nome vale "Ana", e o PHP para ali.
??, isset e o ternário
Antes do PHP 7.0 não existia ??. O jeito de escrever "valor ou plano B" era com a função isset ("está definido?") e o operador ternário (condição ? se sim : se não, que tem aula própria):
<?php $apelido = null; echo isset($apelido) ? $apelido : "sem apelido", "\n"; echo $apelido ?? "sem apelido", "\n"; var_dump(isset($apelido)); var_dump(isset($nome)); $nome = "Ana"; var_dump(isset($nome));
$ php isset.php sem apelido sem apelido bool(false) bool(false) bool(true)
- isset($apelido)
- Responde
truese a variável existe e não énull. Aqui,false: ela existe, mas valenull. - ? $apelido : "sem apelido"
- O ternário: se o
issetdeutrue, usa$apelido; senão,"sem apelido". - $apelido ?? "sem apelido"
- Faz exatamente a mesma coisa, e você escreve o nome da variável uma vez só.
Em outras palavras: $a ?? $b é um atalho para isset($a) ? $a : $b. Você ainda vai encontrar a forma longa em código antigo. Em código novo, prefira o ??. Use o isset sozinho quando a pergunta for só "existe?", sem plano B (por exemplo, numa decisão com if).
Cuidado: ?? só se importa com null
O ?? não troca 0, "" (texto vazio) nem false. Esses valores existem e não são null, então o ?? os mantém. Existe outro atalho, o ?: (apelidado de "operador Elvis", porque ?: lembra o topete do cantor), que troca qualquer valor que vale false. Compare:
<?php $quantidade = 0; $cupom = ""; var_dump($quantidade ?? 1); var_dump($quantidade ?: 1); var_dump($cupom ?? "nenhum"); var_dump($cupom ?: "nenhum");
$ php zero.php int(0) int(1) string(0) "" string(6) "nenhum"
Se a Ana tem zero itens no carrinho, o ?: "corrigiu" para 1, o que é um erro. O ?? respeitou o zero. E tem mais uma diferença: o ?: mostra o Warning quando a variável não existe:
<?php echo $sobrenome ?: "sem sobrenome", "\n";
$ php elvis.php Warning: Undefined variable $sobrenome in /home/ana/site/elvis.php on line 2 sem sobrenome
Qual usar?
| Você quer… | Use | Exemplo |
|---|---|---|
Um valor padrão quando a variável ou a chave não existe ou é null | ?? | $aluna["email"] ?? "não informado" |
Um valor padrão também para "", 0 e false (a variável com certeza existe) | ?: | $titulo ?: "Sem título" |
Só saber se existe (true/false) | isset() | isset($aluna["email"]) |
A mesma coisa que o ??, mas em código antigo (PHP 5) | isset() ? : | isset($a) ? $a : "x" |
??=: preencher só o que está faltando PHP 7.4+
Às vezes você não quer só mostrar um valor padrão, e sim guardá-lo na variável, mas apenas se ela ainda estiver vazia (null ou inexistente). É o ??=, uma atribuição combinada, como +=:
<?php $tema = null; $tema ??= "claro"; echo $tema, "\n"; $tema ??= "escuro"; echo $tema, "\n"; $config = ["idioma" => "pt-BR"]; $config["idioma"] ??= "en"; $config["moeda"] ??= "BRL"; print_r($config);
$ php atrib.php
claro
claro
Array
(
[idioma] => pt-BR
[moeda] => BRL
)
- $tema ??= "claro";
- Atalho de
$tema = $tema ?? "claro";. Como$temaeranull, passou a valer"claro". - $tema ??= "escuro";
$temajá tem valor, então nada muda: continua"claro".- $config["idioma"] ??= "en";
- O idioma já existia (
pt-BR), ficou como estava. - $config["moeda"] ??= "BRL";
- A chave
moedanão existia: foi criada comBRL, sem Warning. Oprint_rmostra o array por dentro (veja var_dump e print_r).
É o jeito ideal de preencher configurações com valores padrão: o que a pessoa escolheu fica; o que ela não escolheu ganha o padrão.
O operador nullsafe ?-> PHP 8.0+
Este operador funciona com objetos, que você vai estudar a fundo no Nível 3 (Classes e Objetos). Por enquanto, basta saber: um objeto é um valor que tem funções próprias, chamadas de métodos, e você chama um método com a seta ->. O PHP já vem com vários objetos prontos; um deles é a data, DateTime, que tem o método format para escrever a data do jeito que você quiser.
A lojinha da Ana guarda a data de entrega de cada pedido. Quando a entrega ainda não foi marcada, a variável vale null. Veja o que acontece:
<?php
$entrega = new DateTime("2026-10-20");
echo $entrega->format("d/m/Y"), "\n";
$entrega = null;
echo $entrega->format("d/m/Y"), "\n";
echo "fim\n";
$ php entrega.php
20/10/2026
Fatal error: Uncaught Error: Call to a member function format() on null in /home/ana/site/entrega.php:6
Stack trace:
#0 {main}
thrown in /home/ana/site/entrega.php on line 6
Agora não é um simples aviso: é um erro fatal, e o programa parou (o "fim" nunca apareceu). A mensagem diz "chamada a um método format() em null": você pediu para o "nada" se formatar.
O ?-> (seta com interrogação) resolve: se o valor da esquerda for null, ele não chama o método e devolve null. Junto com o ??, vira um plano B completo:
<?php
$entrega = new DateTime("2026-10-20");
var_dump($entrega?->format("d/m/Y"));
$entrega = null;
var_dump($entrega?->format("d/m/Y"));
echo "Entrega: " . ($entrega?->format("d/m/Y") ?? "a combinar"), "\n";
$ php entrega2.php string(10) "20/10/2026" NULL Entrega: a combinar
- $entrega
- Pode ser um objeto de data ou
null. - ?->
- "Se não for
null, chame o método; se for, pare aqui e devolvanull." - format("d/m/Y")
- O método chamado quando há data: escreve dia/mês/ano.
- ?? "a combinar"
- Se o resultado foi
null, usa o plano B.
Sem o ?->, era preciso perguntar antes: $entrega !== null ? $entrega->format("d/m/Y") : null. Em cadeias longas (objeto dentro de objeto), isso virava vários if. Com o nullsafe, $pedido?->cliente?->endereco?->cidade para no primeiro null que encontrar.
Na web: $_GET["nome"] ?? "visitante"
Este é o uso mais comum do ?? na vida real. Quando alguém abre ola.php?nome=Ana, o PHP coloca o nome num array especial chamado $_GET. Mas a pessoa pode abrir só ola.php, sem nada, e aí a chave "nome" não existe. A aula $_GET e $_POST explica tudo em detalhes; aqui fica só a ideia.
<?php $nome = $_GET["nome"] ?? "visitante"; echo "<p>Olá, " . htmlspecialchars($nome) . "!</p>\n";
Testando com o servidor embutido e o curl (que abre a página pelo terminal), com e sem o nome na URL:
$ curl -s "localhost:8000/ola.php?nome=Ana" <p>Olá, Ana!</p> $ curl -s localhost:8000/ola.php <p>Olá, visitante!</p>
Sem o ?? (com $nome = $_GET["nome"];), a página aberta sem o nome mostra os avisos para o visitante, em HTML:
$ curl -s localhost:8000/ola-sem.php <br /> <b>Warning</b>: Undefined array key "nome" in <b>/home/ana/site/ola-sem.php</b> on line <b>2</b><br /> <br /> <b>Deprecated</b>: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in <b>/home/ana/site/ola-sem.php</b> on line <b>3</b><br /> <p>Olá, !</p>
O htmlspecialchars está ali por segurança: ele impede que alguém coloque HTML (ou JavaScript) na URL e o seu site o mostre. Veja Escapar a Saída. Experimente: a caixa abaixo funciona como uma página de verdade, aberta no endereço index.php?nome=Ana.
Rode e veja o nome que veio da URL. Depois troque $_GET["nome"] por $_GET["apelido"] (uma chave que não está no endereço) e rode de novo: aparece o plano B, sem Warning. Por fim, tire o ?? "visitante" e veja o aviso que o visitante receberia.
<?php $nome = $_GET["nome"] ?? "visitante"; echo "<p>Olá, " . htmlspecialchars($nome) . "!</p>\n";
Quando dá errado
$ php entrega.php
20/10/2026
Fatal error: Uncaught Error: Call to a member function format() on null in /home/ana/site/entrega.php:6
Stack trace:
#0 {main}
thrown in /home/ana/site/entrega.php on line 6
O que aconteceu: a variável antes da seta -> vale null, e null não tem métodos. O programa parou na linha 6.
Como resolver: se o null é esperado (a entrega pode não estar marcada), use ?-> e um ?? para o plano B. Se não deveria ser null, descubra de onde veio o null (veja Depurar).
0 ou ""
$ php zero.php int(0) int(1) string(0) "" string(6) "nenhum"
O que aconteceu: o ?? só troca null e o que não existe. Zero, texto vazio e false são valores de verdade para ele.
Como resolver: se você quer trocar também o vazio, use ?: (e garanta que a variável existe). Se o zero é um valor válido, como uma quantidade, o ?? está certo.
?? na linha
$ php telefone.php Nome: Ana E-mail: ana@exemplo.com Warning: Undefined array key "telefone" in /home/ana/site/telefone.php on line 6 Telefone:
O que aconteceu: o ?? não está "encostado" na variável. Ou ele foi esquecido, ou está fora de parênteses depois de um ., e aí o PHP leu a variável antes de chegar no ??.
Como resolver: "Telefone: " . ($aluna["telefone"] ?? "não informado"). O mesmo acontece se a variável estiver dentro de uma função: strtoupper($apelido) ?? "X" avisa, porque a função lê o valor primeiro. Escreva strtoupper($apelido ?? "x").
$ php funcao.php Warning: Undefined variable $apelido in /home/ana/site/funcao.php on line 2 Deprecated: strtoupper(): Passing null to parameter #1 ($string) of type string is deprecated in /home/ana/site/funcao.php on line 2
O que aconteceu: a linha era echo strtoupper($apelido) ?? "SEM APELIDO", "\n";. O valor null foi entregue a uma função que espera texto. Deprecated quer dizer "obsoleto": hoje é só um aviso, mas numa versão futura do PHP vai virar erro. E o plano B nem foi usado, porque strtoupper devolveu um texto vazio, não null.
Como resolver: dê o plano B antes de entregar à função: strtoupper($apelido ?? "sem apelido").
Referência
| Operador | Nome | O que faz | Desde |
|---|---|---|---|
$a ?? $b | Coalescência nula | $a se existir e não for null; senão $b. Sem Warning. | PHP 7.0 |
$a ?? $b ?? $c | Em cadeia | O primeiro que existir e não for null. | PHP 7.0 |
$a ??= $b | Atribuição de coalescência nula | Guarda $b em $a só se $a não existir ou for null. | PHP 7.4 |
$obj?->metodo() | Nullsafe | Chama o método se $obj não for null; senão devolve null. | PHP 8.0 |
$a ?: $b | "Elvis" (ternário curto) | $a se ele valer true; senão $b. Avisa se $a não existir. | PHP 5.3 |
isset($a) | "Está definido?" | true se $a existe e não é null. | sempre |
Exercícios
1. Plano B
Complete para mostrar a cidade, ou não informada se ela não existir:
2. Preencher o que falta
Complete para o tema virar "claro" só se ainda não tiver valor:
3. Objeto que pode ser null
Complete para não dar erro fatal quando $entrega for null:
4. $quantidade = 0; O que var_dump($quantidade ?? 1); mostra?
5. A variável $x nunca foi criada. Qual linha mostra padrão sem nenhum Warning?
6. $a ?? $b é um atalho para qual forma longa?
Missão final: a ficha do cliente
Missão: siga os três comentários. (1) Mostre o apelido ou, se ele for null, o nome do cliente. (2) Mostre não informada se a cidade for null. (3) Use ??= para guardar "Brasil" no país, que ainda não existe. No fim, nenhum Warning.
<?php $cliente = ["nome" => "Ana", "cidade" => null]; $apelido = null; // 1. Use o apelido; se ele for null, use o nome do cliente echo "Cliente: " . $apelido . "\n"; // 2. Se a cidade for null ou não existir, mostre "não informada" echo "Cidade: " . $cliente["cidade"] . "\n"; // 3. Se o cliente não tiver país, guarde "Brasil" nele (use ??=) echo "País: " . $cliente["pais"] . "\n";
<?php $cliente = ["nome" => "Ana", "cidade" => null]; $apelido = null; // 1. Use o apelido; se ele for null, use o nome do cliente echo "Cliente: " . ($apelido ?? $cliente["nome"]) . "\n"; // 2. Se a cidade for null ou não existir, mostre "não informada" echo "Cidade: " . ($cliente["cidade"] ?? "não informada") . "\n"; // 3. Se o cliente não tiver país, guarde "Brasil" nele (use ??=) $cliente["pais"] ??= "Brasil"; echo "País: " . $cliente["pais"] . "\n";
Resumo
$a ?? $b: usa$ase ele existir e não fornull; senão,$b. Não mostra Warning para variável ou chave inexistente.- É o atalho de
isset($a) ? $a : $b. Pode ser encadeado:$a ?? $b ?? "padrão". - O
??mantém0,""efalse. Para trocá-los também, existe o?:, que avisa se a variável não existe. $a ??= $bguarda o padrão só no que está faltando.$obj?->metodo()PHP 8.0+ devolvenullem vez de darCall to a member function … on null.- No meio de um texto com
., coloque o??entre parênteses. - Na web:
$_GET["nome"] ?? "visitante", sempre comhtmlspecialcharsna saída.