Bucles de eventos con ReactPHP
Ejecute E/S no bloqueante con el bucle de eventos de ReactPHP.
Bucles de eventos con ReactPHP es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de PHP Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de PHP Academy incluye 4 lecciones en total.
ReactPHP en una frase
ReactPHP es un conjunto de herramientas para E/S no bloqueante escrito exclusivamente en PHP y sin dependencias, basado en un único bucle de eventos. No requiere ninguna extensión: funciona con PHP estándar mediante stream_select (o ext-event/ext-ev cuando están disponibles). Con él puede escribir servidores de larga duración, clientes HTTP concurrentes y canalizaciones de streaming usando solo PHP.
El bucle global
El ReactPHP moderno (v1.2+) expone un bucle predeterminado mediante la fachada estática React\EventLoop\Loop, por lo que no es necesario pasar una instancia del bucle por todas partes. Programe un callback con un temporizador; el bucle se ejecutará hasta que no quede más trabajo.
<?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 y trabajo periódico
La medición de tiempo no bloqueante sustituye a sleep(). addTimer se ejecuta una vez; addPeriodicTimer se repite. Puede cancelar cualquiera de los dos para detenerlo. Como funcionan mediante el bucle, las demás tareas siguen ejecutándose mientras el temporizador espera.
<?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
}
});Promesas para resultados asíncronos
ReactPHP devuelve una promesa para cualquier operación que termine más adelante. Asocie then() para gestionar el éxito y un segundo callback para los errores. Las promesas se pueden componer, de modo que puede transformar y encadenar resultados sin anidar callbacks en exceso.
<?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 concurrente con el cliente asíncrono
La gran ventaja es poder iniciar muchas solicitudes HTTP concurrentemente desde un solo hilo. React\Http\Browser devuelve promesas; React\Promise\all() espera a que termine todo el lote. Diez solicitudes de 1 segundo terminan en aproximadamente 1 segundo, no en 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";
});Las Fibers hacen que el código se lea como síncrono
Las cadenas de callbacks se vuelven difíciles de manejar. ReactPHP se integra con react/async, que utiliza Fibers de PHP 8.1 para que pueda ejecutar await() sobre una promesa y escribir código lineal. El wrapper coroutine()/async() ejecuta su función dentro de una fiber.
<?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();Un servidor TCP no bloqueante
ReactPHP destaca en los servidores. Un SocketServer acepta conexiones y emite eventos; cada conexión es a su vez un stream no bloqueante. Un solo proceso gestiona miles 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";Un servidor HTTP en unas pocas líneas
El servidor de react/http gestiona las solicitudes con un callback que devuelve una Response (o una promesa que contiene una). Como es un proceso persistente, las dependencias se inicializan una sola vez; pero también debe hacerse cargo de la gestión de memoria entre solicitudes.
<?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";Streaming sin almacenamiento en búfer
Los streams de ReactPHP implementan contrapresión: puede usar pipe() para conectar un stream legible con uno escribible y el runtime regula el flujo automáticamente. Esto permite mover gigabytes utilizando una cantidad de memoria pequeña y constante, en lugar de cargar los archivos en la 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 fileEl pecado capital: bloquear el bucle
Todo se rompe en cuanto se llama a una función bloqueante dentro de un handler: sleep(), PDO bloqueante, curl_exec o file_get_contents sobre una URL. Estas funciones congelan el único hilo y detienen todas las conexiones. Utilice los equivalentes de React: Loop::addTimer para las esperas, clue/reactphp-* o controladores de base de datos asíncronos para las consultas, y 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/coroutineLimitar la concurrencia
Un fan-out sin límites puede agotar los descriptores de archivo o sobrecargar una API posterior. clue/mq-react de ReactPHP pone en cola el trabajo con un límite de concurrencia: procesa N promesas a la vez mientras espera el resto. Esto aplica contrapresión a su propia carga saliente.
<?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";
});Comprobación rápida
¿Cómo puede hacer una pausa de 2 segundos dentro de un handler de ReactPHP sin perjudicar la concurrencia?
Resumen
ReactPHP incorpora E/S no bloqueante a PHP estándar:
- Un único bucle de eventos (fachada
Loop) gestiona temporizadores, sockets y streams; no se necesita ninguna extensión. - Las operaciones devuelven promesas;
all()ejecuta las solicitudes en paralelo, yreact/async+ Fibers le permiten usarawait()para escribir código con apariencia síncrona. - Cree servidores TCP/HTTP persistentes y streams con contrapresión capaces de gestionar miles de conexiones por proceso.
- No bloquee nunca el bucle: sustituya
sleepy la E/S bloqueante por temporizadores de React y controladores asíncronos.
Preguntas frecuentes
¿La lección «Bucles de eventos con ReactPHP» es gratis?
Sí — el texto completo de «Bucles de eventos con ReactPHP» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de PHP Academy, actualiza a CoddyKit PRO. El curso de PHP Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Bucles de eventos con ReactPHP»?
Ejecute E/S no bloqueante con el bucle de eventos de ReactPHP. Practicas PHP Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar PHP Academy?
No se requiere experiencia previa. PHP Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Bucles de eventos con ReactPHP»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de PHP Academy?
Sí. Cada lección de PHP Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- El modelo de concurrencia de PHP
- Fibers de PHP 8.1
- Bucles de eventos con ReactPHP
- Servidores de alto rendimiento con Swoole