Primeiramente, preciso te contar que esse artigo não existe na documentação oficial do PHP. Eu inventei esse título - o que pode parecer genial e ao mesmo tempo estúpido - baseado no que acredito fazer sentido sobre o assunto da vez. E por ser um nome inventado, depois de ler tudo, comenta aí se fez sentido, pode ser? Obrigado desde já.
Esse é o último artigo da seção que fala sobre Operadores e eu já falei sobre tudo o que será mencionado aqui.
Ué, Kiko... Se você já falou, por que escrever um artigo?
Por que o artigo onde escrevi foi o Operador de Controle de Erro, o que pode levar a entender que os operadores que quero apresentar só servem para evitar erros e não é esse o ponto. Eu apenas mencionei formas de se fazer tratativas, assim como faço em todo artigo, não é mesmo?
Então hoje nós vamos falar sobre dois operadores relativamente recentes na linguagem:
- null coalescing operator, que chamarei de operador de alternativa de anulidade (mas a tradução correta é operador de coalescência nula);
- nullsafe operator, que vou traduzir para operador de prevenção de anulidade.
Vale ressaltar que ninguém os chama como as traduções que fiz, então se for comentar com alguém, usa o nome em inglês, beleza? Gosto de traduzir dessa forma para que iniciantes possam ter uma ideia clara do motivo da existência desses operadores e ter uma certa clareza no próprio nome.
Começando pela ordem cronológica de lançamentos...
Operador ??
(alternativa de anulidade)
Esse operador surgiu na versão 7 do PHP e veio para abalar as equações ternárias. Basicamente, existia um monte de apliações PHP que, para evitar valores negativos (os false
quando convertido para booleano), escrevia ternários redundantes. Por exemplo:
<?php
$pessoa = ['nome' => null]; // essa pessoa não tem nome :O
$nomeDaPessoa = $pessoa['nome'] ? $pessoa['nome'] : 'Sem Nome';
var_dump($nomeDaPessoa); // string(Sem Nome);
Para quem não sabe, uma equação ternária é o equivalente a if-else
que serve somente para atribuir um valor numa mesma variável. O código acima é o equivalente a:
<?php
$pessoa = ['nome' => null];
if ($pessoa['nome']) {
$nomeDaPessoa = $pessoa['nome'];
} else {
$nomeDaPessoa = 'Sem Nome';
}
var_dump($nomeDaPessoa); // string(Sem Nome);
O benéficio de utilizar equação ternária nesses casos é que você não repete a declaração da variável $nomeDaPessoa
duas vezes. E apesar de, nesse exemplo, a ternária estar relativamente legível, na prática não é bem assim.
O padrão é que o ternário acabe sendo uma linha extensa. Tem quem coloque ternários dentro de ternários (confesso que já cometi esse crime). Então a coisa vai ficando feia.
Daí veio o PHP 7 com essa solução maravilhosa. Se a intenção é usar o ternário para dar um comando "use esse valor ou o outro se o primeiro não estiver definido", você pode resumir isso com o operador de alternativa de anulidade (daí essa tradução):
<?php
$pessoa = ['nome' => null];
$nomeDaPessoa = $pessoa['nome'] ?? 'Sem Nome';
var_dump($nomeDaPessoa); // string(Sem Nome)
Com isso, você não precisa repetir o $pessoa['nome']
duas vezes. Além disso, você pode fazer uma cadeia de alternativas, que vão sendo "testadas" da esquerda pra direita (se você não isolar um grupo com parênteses):
<?php
// não me pergunte por que alguém teria três nomes...
$pessoa = ['nome1' => null, 'nome2' => null, 'nome3' => 'Cleyton'];
$nomeDaPessoa = $pessoa['nome1'] ?? $pessoa['nome2'] ?? $pessoa['nome3'] ?? 'Sem Nome';
var_dump($nomeDaPessoa); // string(Cleyton)
Legal?! Então vamos para o próximo.
Operador ?->
(prevenção de anulidade)
Este operador veio ainda mais tarde que anterior, chegando bem fresquinho no PHP 8. Sua principal função é garantir que o que quer que esteja querendo acessar de um objeto só seja acessado se o objeto não for nulo.
Como assim, Kiko?
E lá vamos nós... :hehe:
<?php
class Exemplo
{
public function echo(): void
{
echo 'Deu sorte, gerou uma instância' . PHP_EOL;
}
public static function getInstance(): self|null
{
if (rand(0,1)) {
return null;
}
return new self;
}
}
for($index = 0; $index < 10000; $index++) {
$exemplo = Exemplo::getInstance();
$exemplo->echo();
}
No código acima, você chama o método estático getInstance
para obter uma nova instância da classe Exemplo
. O que acontece é: você tem 50% de chance de receber null
ou a tal nova instância.
O que acontece na linha seguinte se você receber null
? Bom, vai encerrar a execução com um Fatal Error, pois não tem como acessar o método echo
de um null
.
<b>Fatal Error</b>: Uncaught Error: Call to a member function echo() on null in [...]
Então pode ser interessante se prevenir contra esse cenário. E com esse operador isso é extremamente fácil: basta adicionar uma interrogação antes da chamada do método, ficando ?->echo()
:
<?php
class Exemplo
{
public function echo(): void
{
echo 'Deu sorte, gerou uma instância' . PHP_EOL;
}
public static function getInstance(): self|null
{
if (rand(0,1)) {
return null;
}
return new self;
}
}
for($index = 0; $index < 10000; $index++) {
$exemplo = Exemplo::getInstance();
$exemplo?->echo();
}
Assim, quando $exemplo
for null
, o interpretador não irá executar mais nada, apenas retornar null
.
Ele retorna
null
pra quem, Kiko?
Pra quem estiver recebendo o resultado da chamada. No caso acima, como o método echo
é void
, não esperamos retorno algum. Mas poderíamos estar retornando boolean
e esse fluxo adicionaria um caso de exceção que aplica null
.
Dito isso, é preciso estar atento(a) sobre onde você irá aplicar esse operador, pois se o cenário não prever um valor null
, tudo pode virar uma catástrofe, talvez pior do que sem o operador, rs.
Observação importante: esse operador serve SOMENTE para leitura de dados. Se você o chamar num fluxo de atribuição ou tentar criar uma referência a partir dele, será recebido(a) com um belíssimo Fatal Error. Tenta aí:
<?php
class Exemplo
{
public string $nome = 'Cleyton';
public static function getInstance(): self|null
{
if (rand(0,1)) {
return null;
}
return new self;
}
}
$exemplo = Exemplo::getInstance();
$exemplo?->nome = 'Clarissa'; // Fatal Error
// comente a linha de cima pra ver o Fatal Error debaixo
$nome = &$exemplo?->nome; // Fatal Error
Assim como o null coalescing, você também pode fazer uma cadeia de prevenções...
<?php
$pessoa = null; // tá explícito que é null só por causa do exemplo, ok?!
$resultado = $pessoa?->buscaPai()?->buscaTelefone()?->mandaSms('Seu filho bateu nos moleques da escola');
var_dump($resultado); // NULL
E é isso! Um artigo bem curtinho pra conhecer esses operadores que merecem sim um artigo exclusivo pra falar sobre eles.
Curtiu? Comenta e compartilha! Chama os amigos pra estudar PHP e tirar dúvidas comigo, hehe. No próximo artigo, começaremos a próxima seção: Estruturas de Controle.
Inté!