# PHP para Iniciantes: Estruturas de Controle - MATCH

No artigo anterior, falamos um pouco sobre a estrutura `switch`, que nos dá o poder de criar um chaveador ou seletor baseado no valor final de uma expressão. Um dos detalhes que comentei lá, é que a comparação que é feita entre o valor e os `cases` do `switch` é feita de forma não-rígida, ou seja, usa o `==` ao invés do `===`.

> E por que você tá mencionando tudo isso, Kiko?

Porque, na estrutura de hoje, essas são algumas das principais diferenças, além do fato que o `match` funciona mais ou menos como uma função, retornando alguma coisa.

Vale ressaltar: o `match` surgiu super recentemente, na versão 8.0 do PHP. Nós recebemos hoje, 28 de Outubro de 2021, o [lançamento oficial do **PHP 8.1.0-RC5**](https://www.php.net/archive/2021.php), pra você ter ideia de que realmente foi muito recente.

## Reflexão sobre semântica

Não tem como falar de `match` sem pensar nas aplicações que mais usam esse termo no mercado, né? Pode falar que lembrou do *Tinder*, não precisa fazer cara de paisagem.

<center> ![Safadinho...](https://media.giphy.com/media/2w4NjhvoolDikgQL7i/giphy-downsized-large.gif) </center>

A questão é que o Tinder abusou desse termo ao criar o conceito "perfect match", porque a ideia de `match` já é sobre ser exato, preciso, perfeito. É redundância, sabia? Mas tudo bem, produto é produto e os consumidores compraram bem a ideia.

De qualquer forma, vamos esquecer que existe o termo redundante e tratar como exemplo casos onde os parceiros tem exatamente os mesmos gostos. Com isso, pra não deixar o exemplo muito difícil de entender, vamos imaginar que os indivíduos só tem duas escolhas: cor preferida e número favorito.

Nós estamos desenvolvendo um algoritmo que cruze os dados da Juliana, que tem cor preferida **roxo** e número favorito **8**, com as informações de Tom, Rafael e Luan.

- **Tom**: branco, 8;
- **Rafael**: roxo, 3;
- **Luan**: roxo, 8.

*Obs.: nomes em homenagem a alguns amigos, mas essa relação de Tinder é só resenha viu? **Pelamor**, tem gente casada/comprometida aí nessa lista. Até a Ju, inclusive, hahuhauhauhau. Mas saudades, caso acabem lendo isso aqui.*

Visualmente, você percebe que ambos tem algo em comum com a Juliana, mas somente Luan pode passar no `match`.

Tecnicamente falando, mesmo se Rafael tivesse o número favorito `8.0` (`float`), ainda não passaria no `match`.

Dito isso, uma escrita semântica para essa estrutura seria `DADA (expressão), CASO EXATAMENTE IGUAL A (caso[1]) ENTÃO (retorno[1]), CASO EXATAMENTE IGUAL A (caso[2]) ENTÃO (retorno[2]), (...), CASO EXATAMENTE IGUAL A (caso[n]) ENTÃO (retorno[n]), CASO NENHUMA DAS OPÇÕES ANTERIORES, (retorno[n+1])`.

> Ué, Kiko, isso não a mesma coisa que o `switch`?

Não **exatamente**. Tem algumas diferenças:

- `switch` compara com `==`, `match` compara com `===`;
- `match` retorna o valor final da expressão selecionada, quando possível (você pode disparar *Exceptions*, daí não teria o que retornar, não é mesmo?);
- `match` tem uma sintaxe parecida, mas fundamentalmente diferente. Ela não recebe um bloco de instruções, por exemplo, mas aceita expressões que gerem um valor final. É como se fosse um dicionário de **arrow functions**.

## A sintaxe

Assim como `switch`, o `match` inicia sua sintaxe com a declaração da estrutura e a expressão que será a base das comparações, ficando `match($expressao)`. Seu bloco de código também é encapsulado por chaves (`{ }`). Aqui não tem sintaxe alternativa (a que chamo de *procedural*), então pode se prender às chaves mesmo.

Porém, sua sintaxe interna é mais conectada a estrutura de **arrays**, aquela escrita `chave => valor`, onde tanto a chave quanto o valor podem ser expressões.

> Hmmm... Se eu preciso informar uma chave em tudo, como eu determino a saída padrão, Kiko?

Usando a palavra reservada `default` na chave! Além disso, assim como o `switch`, é possível atribuir o mesmo valor para mais de um caso. Mas a forma de escrever isso é diferente: você pode listar várias chaves separando por vírgulas, escrevendo `chave1, chave2 => valor`. Vou bolar outro caso pra mostrar isso mais abaixo.

Dito tudo isso, nosso primeiro exemplo de hoje pode ser escrito assim:

```php
<?php

$juliana = ['roxo', 8];

$tom = ['branco', 8];
$rafael = ['roxo', 8.0]; // coloquei o 8.0 pra validar o ===
$luan = ['roxo', 8];

$resultado = match($juliana) {
    $tom => 'deu match com Tom!',
    $rafael => 'deu match com Rafa!',
    $luan => 'deu match com Luan!',
    default => 'não deu match com ninguém...'
};

var_dump($resultado); // string(deu match com Luan!)
```

Veja que, com `switch`, teria dado **Rafael**:

```php
<?php

$juliana = ['roxo', 8];

$tom = ['branco', 8];
$rafael = ['roxo', 8.0]; // coloquei o 8.0 pra validar o ==
$luan = ['roxo', 8];

switch ($juliana) {
    case $tom:
        $resultado = 'deu switch com Tom!';
        break;
    case $rafael:
        $resultado = 'deu switch com Rafa!';
        break;
    case $luan:
        $resultado = 'deu switch com Luan!';
        break;
    default:
        $resultado = 'não deu switch com ninguém...';
        break;
}

var_dump($resultado); // string(deu switch com Rafa!)
```

Com isso, comprovamos a grande diferença no comparador usado por baixo no `switch` e no `match`, certo? Mas eu ainda não falei sobre usar mais de um caso para o mesmo valor. Para isso, vamos imaginar o seguinte caso de uso:

> Eu, como **consumidor**,

> quero saber o **status** do meu pedido,

> para poder acompanhar o fluxo até a entrega.

Isso é bem comum em vários projetos, certo? Você pode ter pensado em **pedido de comida**, mas na verdade pode ser até uma compra on-line, de qualquer objeto. Uma Sexy Shop, por exemplo, pode ter esse caso de uso em sua loja on-line. Então isso está muito vago, concorda? Mas já que mencionei Sexy Shop, vamos continuar com esse caso.

<center> ![Buy Buy Buy Buy](https://media.giphy.com/media/MQuNaGVm9qT1ryKWH4/giphy.gif) </center>

Nessa loja, quando um pedido é criado, algumas coisas precisam ser levadas em consideração:

1. O local de entrega do meu cliente é próximo à loja?
2. O(s) produto(s) do pedido está(ão) disponível(eis) em meu estoque?
3. Meu cliente deseja uma embalagem discreta?

No momento em que o indivíduo se faz essas perguntas, nós concluímos que o cliente já:

1. Iniciou um pedido;
2. Adicionou produtos no pedido;
3. Incluiu endereço de entrega;
4. Efetuou pagamento (nós nem mencionamos pagamento ali em cima, então vamos supor que já está pago).

> Mas Kiko, por que raios você tá mencionando tudo isso?

Pra evidenciar que o pedido passou por diversos pontos-chave e já podemos prever outras partes do processo. O pedido foi:

1. Iniciado;
2. Endereçado;
3. Pago.

E o pedido irá ser:

1. Embalado;
2. Despachado;
3. Entregue.

São seis **status** pra generalizar o caso de uso. Vamos desde já criar as constantes que representarão esses seis status no código:

```php
<?php // CartStatusEnum.php

class CartStatusEnum
{
    public const STARTED = 'iniciado';
    public const ADDRESSED = 'endereçado';
    public const PAID = 'pago';
    public const PACKED = 'embalado';
    public const DISPATCHED = 'despachado';
    public const DELIVERED = 'entregue';
}
```

Podemos ver em alguns projetos por aí que é comum exibir todos os **status** de forma transparente para o consumidor, mas há quem prefira só exibir status importantes, que será o que faremos agora. Nós queremos que o cliente veja somente os status:

1. **Aberto**: quando o pedido está **iniciado**;
2. **Aguardando pagamento**: quando o pedido está **endereçado**;
3. **Entregando**: quando o pedido está **pago**, **embalado** ou **despachado**;
4. **Entregue**, que é o último status mesmo.

Precisaremos, assim, transformar seis status em quatro.

Antes de pensarmos em como fazer essa transformação, vamos criar a classe com as constantes dos status públicos:

```php
<?php // CartPublicStatusEnum.php

class CartPublicStatusEnum
{
    public const OPEN = 'aberto';
    public const WAITING_PAYMENT = 'aguardando pagamento';
    public const DELIVERING = 'entregando';
    public const DELIVERED = 'entregue';
}
```

Note que o objetivo aqui não é **traduzir** e sim modificar os valores de`CartStatusEnum` para `CartPublicStatusEnum`.

### Como seria isso com `switch`?

```php
<?php // index.php
include_once __DIR__ . '/CartStatusEnum.php';
include_once __DIR__ . '/CartPublicStatusEnum.php';

function getPublicStatus(string $cartStatus): string
{
    switch($cartStatus) {
        case CartStatusEnum::STARTED:
            return CartPublicStatusEnum::OPEN;
        case CartStatusEnum::ADDRESSED:
            return CartPublicStatusEnum::WAITING_PAYMENT;
        case CartStatusEnum::PAID:
        case CartStatusEnum::PACKED:
        case CartStatusEnum::DISPATCHED:
            return CartPublicStatusEnum::DELIVERING;
        case CartStatusEnum::DELIVERED:
            return CartPublicStatusEnum::DELIVERED;
        default:
            throw new Exception('Status inválido.');
    }
}

echo getPublicStatus(CartStatusEnum::STARTED) . PHP_EOL; // 'aberto'
echo getPublicStatus(CartStatusEnum::ADDRESSED) . PHP_EOL; // 'aguardando pagamento'
echo getPublicStatus(CartStatusEnum::PAID) . PHP_EOL; // 'entregando'
echo getPublicStatus(CartStatusEnum::PACKED) . PHP_EOL; // 'entregando'
echo getPublicStatus(CartStatusEnum::DISPATCHED) . PHP_EOL; // 'entregando'
echo getPublicStatus(CartStatusEnum::DELIVERED) . PHP_EOL; // 'entregue'
```

Parece legal e otimizado? Pois veja a mesma coisa em `match`!

### Como seria com `match`?


```php
<?php // index.php
include_once __DIR__ . '/CartStatusEnum.php';
include_once __DIR__ . '/CartPublicStatusEnum.php';

function getPublicStatus(string $cartStatus): string
{
    return match($cartStatus) {
        CartStatusEnum::STARTED => CartPublicStatusEnum::OPEN,
        CartStatusEnum::ADDRESSED => CartPublicStatusEnum::WAITING_PAYMENT,
        CartStatusEnum::PAID, CartStatusEnum::PACKED, CartStatusEnum::DISPATCHED => CartPublicStatusEnum::DELIVERING,
        CartStatusEnum::DELIVERED => CartPublicStatusEnum::DELIVERED,
        default => throw new Exception('Status inválido.')
    };
}

echo getPublicStatus(CartStatusEnum::STARTED) . PHP_EOL; // 'aberto'
echo getPublicStatus(CartStatusEnum::ADDRESSED) . PHP_EOL; // 'aguardando pagamento'
echo getPublicStatus(CartStatusEnum::PAID) . PHP_EOL; // 'entregando'
echo getPublicStatus(CartStatusEnum::PACKED) . PHP_EOL; // 'entregando'
echo getPublicStatus(CartStatusEnum::DISPATCHED) . PHP_EOL; // 'entregando'
echo getPublicStatus(CartStatusEnum::DELIVERED) . PHP_EOL; // 'entregue'
```

E aí, qual forma está mais legível?

É, PHP 8 está com tudo!... E por hoje é só! Curtiu? **Comenta e compartilha**! Sei que posso ter pecado em considerar somente os **status de sucesso** no exemplo de hoje, mas é só para aplicar algum case, tá bom? Não é pra te ensinar como fazer algo do gênero. A reflexão foi legal, admita. Hahaha.

Enfim, no próximo artigo, falaremos sobre a estrutura `declare`, que apesar de parecer uma função, **não é**. Eu já cheguei a mencioná-la no artigo sobre [declaração de tipagens](https://blog.kaiquegarcia.dev/php-para-iniciantes-tipos-de-dados-primitivos-declarando-tipagens) ao declarar `strict_types=1`, lembra? Mas tem outras coisas que dá pra fazer com ela, então vamos nessa!

**Inté!!**
