# PHP para Iniciantes: Operadores Bitwise

Agora que falamos sobre operadores matemáticos, vamos nos aprofundar um pouco em lógica de programação? Os operadores desse artigo são, basicamente, formas de manipular os valores binários dos dados, **bit a bit**, daí o termo **bitwise**.

Você pode achar que, por se tratar de lógica, isso deve estar relacionado a `boolean`, mas isso não é totalmente verdade, porém também não é totalmente mentira, rs. O fato é que você não vai usar literalmente um `boolean` para fazer essas operações. Nós vamos explorar um pouco os bits, mostrando uma visão booleana para esclarecer o que cada operador executa.

Por exemplo, em lógica, você aprende que `AND` tem a seguinte tabela da verdade:

| `bool` **$a** | `bool` **$b** | `AND` |
| --- | --- | --- |
| `false` | `false` | `false` |
| `false` | `true` | `false` |
| `true` | `false` | `false` |
| `true` | `true` | `true` |

E essa tabela deixa transparente que a operação `AND` só dá `true` quando todos os argumentos envolvidos são `true`. Enquanto `OR`:

| `bool` **$a** | `bool` **$b** | `OR` |
| --- | --- | --- |
| `false` | `false` | `false` |
| `false` | `true` | `true` |
| `true` | `false` | `true` |
| `true` | `true` | `true` |

Transparece que ao menos um dos argumentos precisa ser `true`.

*Obs.: Se você desconhecia essas tabelas da verdade, **recomendo pesquisar sobre** pois, daqui pra frente será necessário que você entenda sobre lógica de programação para ter mais facilidade em entender o conteúdo transmitido, beleza?*

Enfim, conhecendo essas operações, o que acontece com os operadores bitwise é que, ao invés de compararmos dois argumentos simples, nós comparamos o binário entre dois argumentos com um número maior de informações, bit a bit.

Por exemplo, vou deixar aqu uma tabela com números inteiros de 0 a 20 para te mostrar quantos bits cada número representa:

| Inteiro | Binário |
| --- | --- |
| 0 | 0 |
| 1 | 1 |
| 2 | 10 |
| 3 | 11 |
| 4 | 100 |
| 5 | 101 |
| 6 | 110 |
| 7 | 111 |
| 8 | 1000 |
| 9 | 1001 |
| 10 | 1010 |
| 11 | 1011 |
| 12 | 1100 |
| 13 | 1101 |
| 14 | 1110 |
| 15 | 1111 |
| 16 | 10000 |
| 17 | 10001 |
| 18 | 10010 |
| 19 | 10011 |
| 20 | 10100 |

Então, quando usamos um `bitwise AND` entre os inteiros `15` e `20`, o que acontece é:

- **convertemos os inteiros em pacotes de booleanos**;
- `1111` = `(true)(true)(true)(true)`;
- `10100` = `(true)(false)(true)(false)(false)`;
- **extraímos e comparamos cada booleano, da direita pra esquerda**;
- 0: `true and false` = `false`;
- 1: `true and false` = `false`;
- 2: `true and true` = `true`;
- 3: `true and false` = `false`;
- 4: `<null> and true` = `false`;
- **empacotamos o resultado dos bits**: `(false)(false)(true)(false)(false)`;
- **removemos os `false`s à esquerda**: `(true)(false)(false)`;
- **convertemos o novo pacote de booleanos em inteiro**: `100` = `4`;
- ou seja, 15 `bitwise and` 20 = 4!

*Obs.: mais uma vez, se foi díficil de entender, pesquise sobre lógica de programação. Isso chega a ser uma matéria isolada na faculdade, importantíssima pro bom entendimento dessas operações.*

> Ok, Kiko, acho que entendi o que rola no **bitwise**, mas cadê os operadores?

É pra já!

## Operador bitwise `AND`: `&`

No caso, creio que não preciso explicar pois nosso exemplo acima foi sobre isso. Mas sua escrita é simples: `a & b`. No caso que mencionei, poderíamos fazer `$a = 15 & 20`. Se depurar isso, verá que dá 4, como refletimos.


## Operador bitwise `OR`: `|`

Enquanto `&` deixa somente os bits que são igualmente verdade nos argumentos, o `|` vai "somar os 1s". Seguindo nosso exemplo de forma mais resumida, `15 | 20`:

- `15` = `1111`;
- `20` = `10100`;
- `15 | 20` = `11111` (31).

## Operador bitwise `XOR`: `^`

**Xor** é similar a **Or**, exceto que ele é **exclusivo** (daí o X na frente). **Ou exclusivo** é uma operação que considera somente uma das verdades, jamais as duas ao mesmo tempo. Por tanto, em `15 ^ 20` temos:

- `15` = `1111`;
- `20` = `10100`;
- `15 ^ 20` = `11011` (27), apenas o `1` que tínhamos em comum virou 0.

## Operador bitwise `NOT`: `~`

Se o **NOT** booleano (`!`) converte `true` pra `false` e `false` pra `true`, o **NOT** bit a bit deve negar cada bit, o que significa que, diferente dos operadores anteriores, este só afeta um argumento.

Outro detalhe é que a negação não afeta somente os "bits visíveis" do dado, mas sim todo o comprimento. No caso, se você está rodando o PHP num sistema 64 bits, seu inteiro vai ter 64 bits. Então, ao negar **qualquer inteiro**, você estará negando **64 bits**, não somente a parte com `1s` visíveis.

> O que é isso que você chama de bit visível, Kiko?

É o que a gente consegue ver antes do primeiro *zero à esquerda*. Uma boa forma de "enxergar" isso é usar a função `decbin` do PHP, que exibe o binário de um número decimal. Recomendo usar isso para validar os exemplos abaixo.

> Qual a consequência de negar todo o comprimento do inteiro, Kiko?

O interpretador usa o complemento inteiro dos números para detectar seus valores negativos. Por exemplo, se `1` positivo é `1`, o `-1` é o preenchimento de todos bits restantes com `1`. Portanto, negar um inteiro positivo é certo de que irá gerar um inteiro negativo. Vamos ao exemplo?

- `15` = `0000000000000000000000000000000000000000000000000000000000001111`;
- `~15` = `1111111111111111111111111111111111111111111111111111111111110000` (-16);
- `20` = `0000000000000000000000000000000000000000000000000000000000010100`;
- `~20` = `1111111111111111111111111111111111111111111111111111111111101011` (-21).

Parece bizarro mas tá tudo certo.

## Operador bitwise `shift left`: `<<`

Esse é legal pois dá para exemplificar como um fluxo de dados. Por exemplo, se você tem os bits `1010` (10) e quer movê-los um bit para esquerda, fazer `<< 1` transformará `1010` em `10100` (20). *Percebeu que multiplicou por 2?* Cada deslocamento é uma multiplicação por 2... Enfim, esse operador é justamente sobre quantas posições você gostaria de mover para a esquerda.

> Kiko, o que acontece se o bit passar do limite de comprimento?

Ele se perde, simples assim. Se o limite do inteiro for 4 bits, por exemplo, `10 << 1` resultaria em `0100` (4), pois o primeiro bit iria pro saco.

> Kiko, você não vai fazer o exemplo `15 << 20`? :hehe:

**Não**, não tô a fim de sofrer hoje, hahahaha. Mover bits 20 vezes é completamente desnecessário para explicar isso.

Apenas gostaria de deixar uma grande observação: o *shift left* preserva o bit de positividade (positivo/negativo), então não move exatamente os 64 bits do nossos exemplos anteriores, apenas 63 **(ou 31 se o seu sistema for 32 bits)**.

## Operador bitwise `shift right`: `>>`

Proporcionalmente igual ao operador anterior, exceto que, ao invés de mover para a esquerda, move para direita. É preciso ressaltar que é muito mais fácil perder dados nesse caso do que o anterior, pois os primeiros bits simplesmente morrem.

Ah, se deslocar para a esquerda significa multiplicar por 2, deslocar para a direita seria dividir, confere? Então vamos testar:

- `20` = `10100`;
- `20 >> 1` = `1010` (10).

E se movêssemos 2 vezes?

- `20 >> 2` = `101` (5).

E 3?

-  `20 >> 3` = `10` (2). Aqui vemos um bit se perder.

> Kiko, isso não seria uma forma otimizada de fazer divisão por 2, comparando com operadores aritméticos?

Parece mesmo, não é? Mas o interpretador do PHP usa aritmética por baixo dessas operações, o que dá no mesmo. Logo, escreva o que deixa o código mais óbvio, beleza?

> E o que acontece se a gente usar esses operadores em texto?

Nem todos vão ter uma tratativa diferente. Especificamente os operadores `&`, `|`, `^` e `~` conseguem usar o binário da tabela ASCII e responder a partir dessa tabela. Logo, é possível fazer um `AND` entre letras para retornar as **binariamente semelhantes**, embora eu desconheça uma aplicação real disso.

O restante dos operadores irá usar o binário do texto e responder um inteiro.

> Ok, Kiko, mas onde raios usaríamos esses operadores?

*Lemme show it.*

## Bitmask

Também conhecido como **bit flags**, bitmask é uma forma de armazenar um conjunto de possibilidades binárias em uma única variável. Nós conseguimos encontrar isso facilmente em configurações de preferências, isto é, checagens de **checkbox** que representam as preferências do usuário, por exemplo:

- [ ] modo escuro (`true/false`);
- [ ] alto contraste (`true/false`);
- [ ] verificado (`true/false`);
- etc.

Quando temos tantas opções, você pode armazenar cada uma delas em variáveis independentes, separadamente, ou em uma única variável usando *bitmask*. Como diz o nome, você vai criar uma *máscara* que diz o que representa cada bit:

- bit 0: modo escuro (`1/0`);
- bit 1: alto contraste (`1/0`);
- bit 2: verificado (`1/0`).

Assim, toda a informação fica junta e você consegue separar. O exemplo prático seria:

```php
<?php

class Preferencias
{
    public const MODO_ESCURO = 0b1; // 1
    public const ALTO_CONTRASTE = 0b10; // 2
    public const VERIFICADO = 0b100; // 4
}

$preferenciaArmazenada = 3; // (011), modo escuro + alto contraste

if ($preferenciaArmazenada & Preferencias::MODO_ESCURO) {
    echo "Tem modo escuro" . PHP_EOL;
}

if ($preferenciaArmazenada & Preferencias::ALTO_CONTRASTE) {
    echo "Tem alto contraste" . PHP_EOL;
}

if ($preferenciaArmazenada & Preferencias::VERIFICADO) {
    echo "Tem selo de verificação" . PHP_EOL;
}
```

Pegue esse exemplo e experimente alterar os valores de `$preferenciaArmazenada` para:

- 0: nenhuma preferência;
- 1: somente modo escuro;
- 2: somente alto contraste;
- 3: modo escuro + alto contraste;
- 4: verificado;
- 5: modo escuro + verificado;
- 6: alto contraste + verificado;
- 7: modo escuro + alto contraste + verificado.

Se você continuar adicionando números, ele vai continuar detectando se tem ou não um dos primeiros 3 bits, a questão é que o teste é desnecessário nessa ocasião.

Com essa máscara de bits, você pode fazer operações ainda mais complexas. Por exemplo, se você quer exibir uma informação somente para quem ativou modo escuro OU alto contraste:

```php
<?php

// cole a classe aqui, hehe

$pref = 3; // vai funcionar para 1, 2, 3, 5, 6 ou 7
$escuroOuAltoContraste = Preferencias::MODO_ESCURO | Preferencias::ALTO_CONTRASTE;

if ($pref & $escuroOuAltoContraste) {
    echo "Olha o brindeeee";
}
```

Para **ou exclusivo**, é só trocar o `|` por `^`. Assim, não aceitará quando tem os dois.

Enfim, uma vez levantei uma discussão sobre isso com os devs lá da Picpay, pois tem umas bibliotecas bem interessantes que criaram uma boa legibilidade de código com bitmask (o que é raro), mas armazenar esse aglomerado de binários pode dificultar as análises de banco de dados. Depurar um erro em produção com um dado assim não ajuda, então a conclusão da discussão foi: **é melhor manter os dados separados e enxergar com mais clareza o que está sendo parametrizado**.

Mas está aí a possibilidade, caso tenha gostado. Você pode encontrar bitmask em projetos de hardware, pois os códigos lá já são bem doidos mesmo. O bitmask nesses casos ajuda bastante a lidar com instruções lógicas com muitos bits de uma só vez.

Por fim... Curtiu? Comenta e compartilha! No próximo artigo falaremos sobre **operadores de comparação**, que não se resumem a `==` e `===`. São conceitos bem mais simples de entender do que o abordado no artigo de hoje. Tá preparado(a)? Só vamo.

**Inté!**
