Introdução à programação assíncrona em PHP
Tradicionalmente, o PHP é considerado uma linguagem síncrona: cada requisição é executada sequencialmente, bloqueando a execução até que a operação de entrada/saída (por exemplo, leitura de arquivo ou consulta ao banco de dados) seja concluída. No entanto, com o crescimento da popularidade de aplicações web de alta carga e microsserviços, surgiu a necessidade de processar dezenas de milhares de conexões simultâneas sem escalar o número de processos. A programação assíncrona resolve esse problema, permitindo executar operações concorrentemente dentro de uma única thread.
Neste guia, vamos analisar como a assincronia funciona em PHP, quais ferramentas estão disponíveis e como construir aplicações não bloqueantes corretamente. Você aprenderá sobre generators, corrotinas, bibliotecas ReactPHP, Amp e Swoole, além da nova extensão Fibers.
Fundamentos da assincronia: Event Loop e entrada/saída não bloqueante
A programação assíncrona baseia-se no conceito de Event Loop (loop de eventos). Trata-se de um loop infinito que aguarda e processa eventos: requisições de rede, conclusão de operações de entrada/saída, timers. Em vez de esperar a conclusão da operação, o código registra um callback que é executado quando os dados estão prontos. Isso evita o desperdício de tempo de CPU com espera.
Tradicionalmente, o PHP utiliza funções bloqueantes (por exemplo, file_get_contents() ou sleep()). Para trabalhar de forma assíncrona, é necessário substituí-las por equivalentes não bloqueantes. Aqui está um exemplo simples usando a biblioteca ReactPHP:
<?php
require 'vendor/autoload.php';
use React\\EventLoop\\Factory;
use React\\Promise\\Promise;
$loop = Factory::create();
// Leitura assíncrona de arquivo
$promise = new Promise(function ($resolve, $reject) use ($loop) {
$loop->addTimer(0.001, function () use ($resolve) {
$content = file_get_contents('/etc/hosts');
$resolve($content);
});
});
$promise->then(function ($data) {
echo "Lido: " . strlen($data) . " bytes";
});
echo "Este código será executado imediatamente, sem esperar a leitura do arquivo";
$loop->run();
?>
Neste exemplo, addTimer registra uma tarefa que será executada no próximo tick do loop. O Event Loop não é bloqueado, e vemos a mensagem imediatamente, enquanto o resultado da leitura aparece depois.
Generators e corrotinas: controle do fluxo de execução
Os generators (yield) surgiram no PHP 5.5 e permitem pausar a execução de uma função, retornando valores intermediários. Com base neles, são construídas as corrotinas — threads leves que podem voluntariamente ceder o controle. Em combinação com o Event Loop, os generators permitem escrever código que parece síncrono, mas é executado de forma assíncrona.
Vamos considerar um exemplo com a biblioteca Amp, que utiliza corrotinas:
<?php
require 'vendor/autoload.php';
use Amp\\Loop;
use Amp\\Delayed;
// Corrotina assíncrona
$coroutine = function () {
echo "Início da corrotina";
// Pausamos por 1 segundo sem bloqueio
yield new Delayed(1000);
echo "Passou 1 segundo";
yield new Delayed(500);
echo "Passaram mais 0.5 segundos";
};
Loop::run(function () use ($coroutine) {
// Iniciamos a corrotina
$result = yield from $coroutine();
echo "Corrotina concluída";
});
?>
Neste código, yield new Delayed(1000) pausa a execução da corrotina, devolvendo o controle ao Event Loop. Após 1 segundo, a execução é retomada exatamente de onde parou. Isso permite processar milhares dessas corrotinas simultaneamente, sem criar novos processos.
Bibliotecas e ferramentas para PHP assíncrono
Para trabalhar com assincronia em PHP, existem várias bibliotecas maduras. Vamos considerar as três mais populares.
ReactPHP
ReactPHP é a biblioteca mais antiga e mais conhecida. Ela fornece Event Loop, Promises (análogas às do JavaScript
```