0Pricing
PHP Academy · Aula

Loops de eventos com ReactPHP

Execute E/S não bloqueante com o loop de eventos do ReactPHP.

Loops de eventos com ReactPHP é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.

ReactPHP em uma frase

ReactPHP é um conjunto de ferramentas em PHP puro e sem dependências para E/S não bloqueante, baseado em um único loop de eventos. Nenhuma extensão é necessária — ele funciona no PHP padrão usando stream_select (ou ext-event/ext-ev quando disponíveis). Com ele, é possível escrever servidores de longa duração, clientes HTTP paralelos e cadeias de transmissão em PHP puro.

O loop global

O ReactPHP moderno (v1.2+) expõe um loop padrão por meio da fachada estática React\EventLoop\Loop — não é necessário passar uma instância do loop por toda parte. Agende uma função de retorno com um temporizador; o loop executa até não haver mais trabalho.

<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;

Loop::addTimer(1.0, function () {
    echo "fired after 1 second\n";
});
echo "scheduled\n";
// Loop::run() is auto-invoked at script shutdown in v1.2+

Temporizadores e trabalho periódico

A temporização não bloqueante substitui sleep(). addTimer é executado uma vez; addPeriodicTimer se repete. Cancele qualquer um deles para interrompê-lo. Como são controlados pelo loop, outras tarefas continuam sendo executadas enquanto o temporizador aguarda.

<?php
use React\EventLoop\Loop;

$count = 0;
$timer = Loop::addPeriodicTimer(0.5, function () use (&$count, &$timer) {
    echo 'tick ' . (++$count) . "\n";
    if ($count === 4) {
        Loop::cancelTimer($timer); // stop after 4 ticks
    }
});

Promessas para resultados assíncronos

O ReactPHP retorna uma promessa para qualquer operação que seja concluída mais tarde. Associe then() ao caso de sucesso e uma segunda função de retorno ao caso de falha. As promessas podem ser compostas, portanto é possível transformar e encadear resultados sem aninhar profundamente as funções de retorno.

<?php
use React\Promise\Promise;

function asyncDouble(int $n): Promise {
    return new Promise(function ($resolve) use ($n) {
        \React\EventLoop\Loop::addTimer(0.1, fn() => $resolve($n * 2));
    });
}

asyncDouble(21)
    ->then(fn($r) => printf("result: %d\n", $r))   // 42
    ->catch(fn(\Throwable $e) => printf("err: %s\n", $e->getMessage()));

HTTP paralelo com o cliente assíncrono

A grande vantagem: disparar muitas requisições HTTP simultaneamente a partir de uma única linha de execução. React\Http\Browser retorna promessas; React\Promise\all() aguarda todo o lote. Dez requisições de 1 segundo terminam em cerca de 1 segundo, não em 10.

<?php
use React\Http\Browser;
use function React\Promise\all;

$browser = new Browser();
$urls = [
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/1',
];
$requests = array_map(fn($u) => $browser->get($u), $urls);

all($requests)->then(function (array $responses) {
    echo 'all done: ' . count($responses) . " responses\n";
});

As fibras tornam a leitura síncrona

As cadeias de funções de retorno se tornam difíceis de gerenciar. O ReactPHP integra-se com react/async, que usa fibras do PHP 8.1 para que seja possível usar await() em uma promessa e escrever código linear. O envoltório coroutine()/async() executa sua função dentro de uma fibra.

<?php
use function React\Async\async;
use function React\Async\await;
use React\Http\Browser;

$main = async(function () {
    $browser = new Browser();
    $a = await($browser->get('https://httpbin.org/uuid'));
    $b = await($browser->get('https://httpbin.org/uuid'));
    echo (string) $a->getBody();
    echo (string) $b->getBody();
});
$main();

Um servidor TCP não bloqueante

O ReactPHP é excelente para servidores. Um SocketServer aceita conexões e emite eventos; cada conexão é, por si só, um fluxo não bloqueante. Um único processo gerencia milhares de clientes simultâneos.

<?php
use React\Socket\SocketServer;
use React\Socket\ConnectionInterface;

$socket = new SocketServer('127.0.0.1:8080');
$socket->on('connection', function (ConnectionInterface $conn) {
    $conn->write("Welcome\n");
    $conn->on('data', function ($data) use ($conn) {
        $conn->write('echo: ' . $data); // streamed back, non-blocking
    });
});
echo "listening on 127.0.0.1:8080\n";

Um servidor HTTP em poucas linhas

O servidor react/http gerencia requisições com uma função de retorno que devolve uma Response (ou uma promessa de uma resposta). Como é um processo persistente, as dependências são inicializadas uma vez — mas o gerenciamento da memória entre as requisições também passa a ser sua responsabilidade.

<?php
use React\Http\HttpServer;
use React\Http\Message\Response;
use Psr\Http\Message\ServerRequestInterface;
use React\Socket\SocketServer;

$http = new HttpServer(function (ServerRequestInterface $request) {
    return Response::json(['path' => $request->getUri()->getPath()]);
});
$http->listen(new SocketServer('0.0.0.0:8000'));
echo "http://0.0.0.0:8000\n";

Transmissão contínua sem armazenamento em buffer

Os fluxos do ReactPHP implementam contrapressão: é possível usar pipe() para conectar um fluxo de leitura a um fluxo de gravação, e o ambiente de execução regula a velocidade automaticamente. Isso permite mover gigabytes usando uma quantidade pequena e constante de memória, em vez de carregar arquivos na RAM.

<?php
use React\Stream\ReadableResourceStream;
use React\Stream\WritableResourceStream;

$source = new ReadableResourceStream(fopen('big.log', 'r'));
$dest   = new WritableResourceStream(fopen('php://stdout', 'w'));
$source->pipe($dest); // back-pressure aware, never buffers whole file

O pecado capital: bloquear o loop

Tudo se rompe no momento em que uma função bloqueante é chamada dentro de um manipulador — sleep(), PDO bloqueante, curl_exec, file_get_contents em uma URL. Essas funções congelam a única linha de execução e interrompem todas as conexões. Use os equivalentes do React: Loop::addTimer para atrasos, clue/reactphp-* ou drivers assíncronos de banco de dados para consultas, e o Browser para HTTP.

<?php
use React\EventLoop\Loop;
use function React\Async\await;
use React\Promise\Deferred;

// BAD:  sleep(2);  // freezes the entire loop
// GOOD: async delay that yields control
function delay(float $s): \React\Promise\PromiseInterface {
    $d = new Deferred();
    Loop::addTimer($s, fn() => $d->resolve(null));
    return $d->promise();
}
// await(delay(2.0)); // inside a fiber/coroutine

Limitando a concorrência

Uma distribuição sem limites pode esgotar descritores de arquivo ou sobrecarregar uma API de destino. O clue/mq-react do ReactPHP enfileira o trabalho com um limite de concorrência, processando N promessas por vez enquanto o restante aguarda. Isso aplica contrapressão à sua própria carga de saída.

<?php
use Clue\React\Mq\Queue;
use React\Http\Browser;

$browser = new Browser();
// At most 10 in-flight requests across 1000 URLs
$q = new Queue(10, null, fn($url) => $browser->get($url));

$promises = array_map(fn($u) => $q($u), $thousandUrls ?? []);
\React\Promise\all($promises)->then(function ($responses) {
    echo 'completed ' . count($responses) . " with cap of 10\n";
});

Verificação rápida

Como pausar por 2 segundos dentro de um manipulador do ReactPHP sem prejudicar a concorrência?

Recapitulação

O ReactPHP leva E/S não bloqueante ao PHP puro:

  • Um único ciclo de eventos (fachada Loop) controla temporizadores, soquetes e fluxos — nenhuma extensão é necessária.
  • As operações retornam promessas; all() executa as solicitações em paralelo, e react/async + fibras permitem usar await() para escrever código com aparência síncrona.
  • Crie servidores TCP/HTTP persistentes e fluxos com controle de contrapressão que lidem com milhares de conexões por processo.
  • Nunca bloqueie o ciclo de eventos — substitua sleep/E/S bloqueante por temporizadores do React e controladores assíncronos.

Perguntas Frequentes

A aula “Loops de eventos com ReactPHP” é grátis?

Sim — o texto completo de “Loops de eventos com ReactPHP” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.

O que vou aprender em “Loops de eventos com ReactPHP”?

Execute E/S não bloqueante com o loop de eventos do ReactPHP. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar PHP Academy?

Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Loops de eventos com ReactPHP”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de PHP Academy?

Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. O modelo de concorrência do PHP
  2. Fibers do PHP 8.1
  3. Loops de eventos com ReactPHP
  4. Servidores de alto desempenho com Swoole
← Voltar para PHP Academy