Swoole: o PHP concorrente
Tags: php, swoole, web, braziliandevs
Published: 2022-03-27 15:22:35

> _"Para sempre é muito tempo. O tempo não pára!"_
> – Mario Quintana

Eu comecei a programar em JavaScript no servidor (Node.js) bem antes de PHP, logo o conceito de assincronismo já estava naturalizado no meu jeito de fazer as coisas pois o JavaScript já é assíncrono por natureza.

Quando comecei a programar em PHP a primeira coisa fora do básico que eu vi foi o funcionamento do php-curl, e gostei do quanto ele era procedural. PHP foi pensado como uma linguagem procedural e posteriormente foi implementa uma solução de orientação a objetos à linguagem. No mundo real, nem sempre podemos esperar que uma operação seja realizada para partir para a próxima, muitas vezes necessitamos que as coisas  ocorram ao mesmo tempo, que pelo menos pareça que ocorram dessa forma.

Já existem técnicas conhecidas para resolver esse problema, nesse artigos usaremos corrotinas, uma oposição ao pthread do PHP. As corrotinas são parecidas com as threads e seu comportamento, porém consomem menos recursos. 

A solução que temos para esse problema é o OpenSwoole, que segundo a documentação do mesmo, ele foi pensando para 
 construir serviços de alto desempenho, escaláveis, TCP, UDP, Unix Socket, HTTP, WebSocket com PHP por meio de corrotinas.

## Instalação do Swoole

A primeira coisa a ser feita é instalar a extensão do Swoole na nossa máquina. Eu recomendo fortemente o uso do PECL, se você não usa amigo, instale agora na sua máquina. Vamos usar a distribuição GNU/Linux Ubuntu como referência.

Caso não possua o PECL instalado:

`$ sudo apt-get install php-pear php-dev
`
 
Em seguida vamos instalar o Swoole:

`$ sudo sudo pecl install openswoole-4.9.1`

Para finalizar iremos habilitar a extensão do Swoole adicionando essa linha ao nosso php.ini

`extension=openswoole.so
`

## Mão na massa

Após instalar e habilitar o swoole uma série de classes e helpers ficam disponíveis para que possamos utiliza-lo em nossas aplicações. Nesse exemplo usaremos o `go();`

A função go() nos permite encapsular uma função dentro de uma corrotina. 

Teremos funções encapsuladas por duas corrotinas, uma lista uma série de heróis de animes, e a outra lista os vilões. TNosso objetivo é que essa listagem ocorra "paralelamente".

Esse exemplo nos permitirá um vislumbre do funcionamento do Swoole.

O código abaixo:

```php

<?php

// Lista os heróis
go(static function () {
    
    $heros = ['Kurosaki Ichigo', 'Uzumaki Naruto', 'Kamado Tanjiro'];

    foreach ($heros as $hero) {
        echo 'Hero: ' . $hero . PHP_EOL;
    }
});

// Lista os vilões
go(static function () {
    
    $villains = ['Sosuke Aizen', 'Otsutsuki Kaguya', 'Muzan Kibutsuji'];
    
    foreach ($villains as $villain){
        echo 'Villain: ' . $villain . PHP_EOL;
    }
});


```
Exibe o seguinte resultado:

```
Hero: Kurosaki Ichigo
Hero: Uzumaki Naruto
Hero: Kamado Tanjiro
Villain: Sosuke Aizen
Villain: Otsutsuki Kaguya
Villain: Muzan Kibutsuji

```

"Mas você não disse que era paralelo?"

Sim, mas você não acha que é um código simples e rápido de mais para se perceber isso? Pois, de fato é. Então vamos adicionar uma pausa no processo de listagem para vermos o Swoole agir.

A única mudança que faremos no código anterior é adicionar o `sleep();` depois de cada `echo`.

```php

<?php

// Lista os heróis
go(static function () {
    
    $heros = ['Kurosaki Ichigo', 'Uzumaki Naruto', 'Kamado Tanjiro'];

    foreach ($heros as $hero) {
        echo 'Hero: ' . $hero . PHP_EOL;
        sleep(1);
    }
});

// Lista os vilões
go(static function () {
    
    $villains = ['Sosuke Aizen', 'Otsutsuki Kaguya', 'Muzan Kibutsuji'];
    
    foreach ($villains as $villain){
        echo 'Villain: ' . $villain . PHP_EOL;
        sleep(1);
    }
});


```
E novamente temos a saida:

```
Hero: Kurosaki Ichigo
Hero: Uzumaki Naruto
Hero: Kamado Tanjiro
Villain: Sosuke Aizen
Villain: Otsutsuki Kaguya
Villain: Muzan Kibutsuji

```
Não mudou nada. A única coisa diferente neesse segundo código foi a espera de 1 segundo para cada elemento ser impresso. Ainda não sentimos a sensação de que de fato as duas funções estão sendo executadas "ao mesmo tempo". A culpa disso é da  função `sleep()`. A função `sleep()` é bloqueante, isso faz com que nosso código seja procedural. para resolvermos isso  executaremos uma função auxiliar do Swoole que faz com que as funções bloqueates do PHP se comportem como não-bloqueantes

```php

<?php

// Método auxiliar que torna o código não-bloqueante bloqueante
Swoole\Runtime::enableCoroutine();


// Lista os heróis
go(static function () {
    
    $heros = ['Kurosaki Ichigo', 'Uzumaki Naruto', 'Kamado Tanjiro'];

    foreach ($heros as $hero) {
        echo 'Hero: ' . $hero . PHP_EOL;
        sleep(1);
    }
});

// Lista os vilões
go(static function () {
    
    $villains = ['Sosuke Aizen', 'Otsutsuki Kaguya', 'Muzan Kibutsuji'];
    
    foreach ($villains as $villain){
        echo 'Villain: ' . $villain . PHP_EOL;
        sleep(1);
    }
});
```
Com isso finalmente temos nosso código sendo executado de modo alternado:

```
Hero: Kurosaki Ichigo
Villain: Sosuke Aizen
Hero: Uzumaki Naruto
Villain: Otsutsuki Kaguya
Villain: Muzan Kibutsuji
Hero: Kamado Tanjiro
```

É isso pessoal, até a próxima :)