PHP para Iniciantes: Operadores de Comparação

Os operadores que iremos abordar hoje são bem simples de se entender, pois o que fazem está estampado no título: comparam. E quando algo é feito para comparar, você espera ter somente duas possibilidades: sim ou não, certo?

No caso da programação, sempre que falamos sobre algo que trabalha com duas possibilidades, estamos nos referindo ao Tipo de Dados Primitivos bool, aquele que naturalmente entendemos como verdade ou falso.

Mas Kiko, como assim o comparador só passa dois valores?

Pensa assim: se você quer comparar algo, você precisa de outra coisa de referência, certo? Afinal, é isso que é uma comparação. Então, independente da pergunta, o que você estiver comparando será uma pergunta afirmativa. Por exemplo:

  • João é mais alto que Maria? sim.
  • Elton tem o mesmo tamanho de Robson? não.

Ah, Kiko, e se eu estiver selecionando um jogador e escolher o mais alto durante a comparação?

Então você primeiro comparou até encontrar a resposta que satisfaz a dúvida: quem é o maior? Essa não pode ser respondida com sim ou não, mas também não é uma comparação, é um pouco mais complexa que isso. De todo modo, se temos João, Maria, Elton e Robson, sendo Robson o mais alto, seu algoritmo faria algo mais ou menos assim:

  1. Quem é o maior? [João, Maria, Elton, Robson]
    • João é maior que Maria? Sim.
      • maior = João.
    • [maior] (João) é maior que Elton? Não.
      • maior = Elton.
    • [maior] (Elton) é maior que Robson? Não.
      • maior = Robson.

Logo, o maior é Robson. E aí sim você escolhe ele pro seu time.

Ou seja, nesse meio tempo, você fez três comparações. Seu cérebro faz isso muitas vezes ao longo do dia sem você sequer perceber. Por exemplo, você já ouviu falar em Pareidolia? É uma condição psicológica específica onde você enxerga rostos em objetos inanimados, como aquele caso famoso do rosto de Jesus na madeira.

Você só enxerga esses rostos pois seu cérebro está o tempo todo fazendo comparações das imagens que você percebe à sua volta com as suas memórias para chegar a uma conclusão. O tempo de reação do seu corpo é relativamente proporcional à capacidade do sr. Cerebrinho perceber um perigo se aproximar. E a pergunta é simples:

  • é perigoso? sim/não.

Ah, Kiko, isso não é uma comparação.

Se você olhar no literal do português, não é mesmo. Mas o que define "perigo" pra você? Seu cérebro sempre vai comparar as situações com as suas lembranças, então todo questionamento que ele faz é uma comparação.

Ah...

MAS NÃO ESTAMOS AQUI PRA FALAR DE VOCÊ, vamos voltar a falar de PHP.

Agora que deixei claro que comparadores existem em todos os lugares, vamos aos operadores:

Operador ==

É o mais clássico, significa "$a é igual a $b?" na expressão $a == $b. Observe que apenas compara seu valor, o que significa que diferenças de tipagens são ignoradas, diferente do próximo.

Operador ===

Não tão conhecido assim, significa "$a é idêntico a $b?" na expressão $a === $b. Quanto a ser idêntico, não basta ter o mesmo valor, mas também as mesmas tipagens e subvalores¹, no caso das classes e/ou arrays.

*¹ - eu inventei esse termo agora, não sei se realmente usam isso. Foi minha forma de expressar "valores internos do dado que está sendo comparado".

Operador != ou <>

Tão clássico quanto o primeiro, significa "$a é diferente de $b?" na expressão $a != $b ou $a <> $b. É a negação do ==, então se ele é true em um, é false no outro.

Obs.: por padrão, costumamos escrever != ao invés de <>, mas a galera de banco de dados prefere a outra escrita, então você pode acabar encontrando essa notação por aí.

Mas por quê esse símbolo, Kiko?

Você verá mais abaixo que < e > são operadores isolados que não aceitam comparar valores iguais. Ao usá-los juntos, você diz explicitamente que o valor pode ser tanto maior quanto menor, só não pode ser igual. Portanto, diferente.

Operador !==

Significa "$a é minimanente diferente de $b?" na expressão $a !== $b. Da mesma forma que a anterior, é a negação do ===. Quando falo minimamente diferente quero dizer que a menor das diferenças são consideradas, inclusive as tipagens.

Operador >

Significa "$a é maior que $b?" na expressão $a > $b. Note que, se $a for igual a $b, a resposta será false, pois para ser maior ela precisa ser diferente.

Dica: Às vezes as pessoas confundem com menor que, mas pensa assim: o símbolo é uma seta ou flecha, onde a ponta fica direcionada para a direita. O lado esquerdo fica com duas pontas... Logo, o lado esquerdo tem mais pontas que o direito: esquerda é maior que a direita => esquerda > direita. Gostou da dica? O mesmo vale pro próximo, só que ao contrário.

Operador <

Significa "$a é menor que $b?" na expressão $a < $b.

Operador >=

Significa "$a é maior ou igual a $b?" na expressão $a >= $b. Dessa vez, combinamos o > com ==, então eles podem ter o mesmo valor para resultar em true.

Operador <=

Significa "$a é menor ou igual a $b?" na expressão $a <= $b. Podemos usar as mesmas observações do operador anterior, onde o comparador mesclado foi o <.

Operador especial: Spaceship <=>

Ué, por que especial, Kiko?

Hehehehe... Eu te disse com muita firmeza que todo comparador definitivamente retorna true ou false, certo? Eis que na versão 7 do PHP criaram um novo operador (apelidado de nave espacial) que serve para questionar: $a é menor que, igual a ou maior que $b? A pergunta deixa de ser respondida por sim ou não, mas por um dado.

Seu funcionamento é bem simples:

  • se o primeiro número for maior que o segundo, retorna 1;
  • se forem iguais, retorna 0;
  • se o primeiro for menor que o segundo, retorna a -1.

Exemplos:

  • 5 <=> 01;
  • 5 <=> 31;
  • 5 <=> 50;
  • 5 <=> 7-1;
  • 5 <=> 10-1.

Como há mais de duas opções, não teríamos como aplicar retornos booleanos nesse caso. Se você tivesse de reescrever esse operador especial de comparação com os não-especiais, seria algo mais ou menos assim:

<?php

function spaceship(int|float $a, int|float $b): int
{
    if ($a > $b) { // comparador 1
        return 1;
    }
    if ($a < $b) { // comparador 2
        return -1;
    }
    // comparador implícito 3 (==)
    return 0;
}

var_dump(spaceship(5, 0)); // int(1)
var_dump(spaceship(5, 3)); // int(1)
var_dump(spaceship(5, 5)); // int(0)
var_dump(spaceship(5, 7)); // int(-1)
var_dump(spaceship(5, 10)); // int(-1)

Ok, Kiko, entendi a reescrita, mas pra que serve esses 1, 0 e -1??

O exemplo mais claro pra mim é o caso de ordenação de arrays. Quando você quer ordenar um array, você tem a opção de usar a função sort ou desenvolver seus próprios critérios com a função usort. Por exemplo, para ordenar um array com os nomes dos primeiros exemplos de forma crescente, basta usar a sort:

<?php

$array = ['João', 'Maria', 'Elton', 'Robson'];

sort($array);
var_dump($array); // [Elton, João, Maria, Robson]

Porém se o seu dado for mais complexo, a função não vai ter o comportamento que você espera. Por exemplo, digamos que nós temos uma classe pessoa e esses caras serão instâncias dela...

<?php

class Pessoa
{
    public function __construct(
        private int $id,
        private string $name
    ) { }

    public function getName(): string
    {
        return $this->name;
    }

    public function __toString(): string
    {
        return "{$this->id}::{$this->name}";
    }
}

$array = [
    new Pessoa(1, 'João'),
    new Pessoa(2, 'Maria'),
    new Pessoa(3, 'Elton'),
    new Pessoa(4, 'Robson'),
];

// o que vai acontecer com o sort()??
sort($array);
var_dump($array);

Respondendo a pergunta no código: NADA. Graças ao método __toString, por colocar o id antes do nome, o array ficou ordenado por id.

Para forçar a ordenar por nome, você vai precisar desenvolver seu próprio ordenador. É aqui que entram os números mágicos 1, 0 e -1:

  • se o nome atual é "maior" que o próximo, ele deve vir depois, então some o índice dele com 1;
  • se forem iguais, some com 0, não mude;
  • se for "menor", some com -1 para que ele fique na frente.
// ... cole depois do var_dump

function ordenar(Pessoa $atual, Pessoa $proxima): int
{
    if ($atual->getName() > $proxima->getName()) {
        return 1;
    }
    if ($atual->getName() < $proxima->getName()) {
        return -1;
    }
    return 0;
}

usort($array, 'ordenar');
var_dump($array); [Elton, João, Maria, Robson]

Achou a função familiar? Pois é! Podemos trocar isso tudo por um spaceship!

// retire o código de cima e coloque esse
function ordenar(Pessoa $atual, Pessoa $proxima)
{
    return $atual->getName() <=> $proxima->getName();
}

usort($array, 'ordenar');
var_dump($array);

Nesse caso, se quiser ordenar decrescente, basta trocar o $atual de lugar com o $proxima!

E por hoje é só! Curtiu? Comenta e compartilha! No próximo artigo falaremos sobre Operadores de Controle de Erro, que é um só e eu espero que você nunca utilize isso em lugar algum. Se você disser "mas...", pode parar por aí. É sério... Eu vou expor meu ponto de vista sobre isso lá, beleza? Obrigado por sua ilustríssima presença!

Inté!