ReactPHP introduce un modelo de I/O no bloqueante en PHP mediante un event loop. En lugar de esperar bloqueado a que un socket responda o un temporizador expire, el loop atiende miles de operaciones simultáneas con un único hilo, igual que Node.js.
Instalación
composer require react/event-loop react/promise react/http react/socket
El event loop
<?php
require 'vendor/autoload.php';
use ReactEventLoopLoop;
// Temporizador que se dispara una vez
Loop::addTimer(2.0, function () {
echo "Han pasado 2 segundosn";
});
// Temporizador periódico
Loop::addPeriodicTimer(1.0, function () {
echo "Tick: " . date('H:i:s') . "n";
});
echo "El loop arranca ahoran";
Loop::run();
?>
Promesas
ReactPHP usa promesas para manejar operaciones asíncronas sin bloquear:
<?php
use ReactPromiseDeferred;
function operacionAsincrona(): ReactPromisePromiseInterface
{
$deferred = new Deferred();
Loop::addTimer(1.0, function () use ($deferred) {
$deferred->resolve('Resultado listo');
// $deferred->reject(new Exception('Algo falló'));
});
return $deferred->promise();
}
$promesa = operacionAsincrona();
$promesa->then(
function (string $resultado) {
echo "Éxito: $resultadon";
},
function (Exception $error) {
echo "Error: {$error->getMessage()}n";
}
);
Loop::run();
?>
Encadenar promesas
<?php
use function ReactPromiseresolve;
resolve(10)
->then(fn($v) => $v * 2) // 20
->then(fn($v) => $v + 5) // 25
->then(fn($v) => "Resultado: $v")
->then(fn($s) => print($s . "n"));
Loop::run();
?>
Servidor HTTP con react/http
<?php
use ReactHttpHttpServer;
use ReactHttpMessageResponse;
use ReactSocketSocketServer;
use PsrHttpMessageServerRequestInterface;
$server = new HttpServer(function (ServerRequestInterface $request) {
$path = $request->getUri()->getPath();
return match ($path) {
'/' => Response::plaintext("Hola desde ReactPHPn"),
'/json' => Response::json(['ok' => true, 'ts' => time()]),
default => new Response(404, [], "No encontradon"),
};
});
$socket = new SocketServer('0.0.0.0:8080');
$server->listen($socket);
echo "Servidor en http://localhost:8080n";
Loop::run();
?>
Streams no bloqueantes
<?php
use ReactSocketConnector;
use ReactSocketConnectionInterface;
$connector = new Connector();
$connector->connect('tcp://example.com:80')->then(
function (ConnectionInterface $conn) {
$conn->write("GET / HTTP/1.0rnHost: example.comrnrn");
$conn->on('data', function (string $data) {
echo substr($data, 0, 200) . "n";
});
$conn->on('close', function () {
echo "Conexión cerradan";
});
},
function (Exception $e) {
echo "Error de conexión: {$e->getMessage()}n";
}
);
Loop::run();
?>
Antipatrones a evitar
- sleep() dentro del loop: bloquea el hilo entero. Usa
Loop::addTimer()en su lugar. - PDO/MySQLi síncronos: las consultas bloquean el loop. Usa
react/mysqlpara consultas no bloqueantes o ejecuta las consultas pesadas en procesos separados. - file_get_contents() en el loop: bloquea en I/O de disco. Usa
react/filesystemo carga los ficheros antes de arrancar el loop.
Concurrencia con ReactPromiseall()
<?php
use function ReactPromiseall;
$promesas = [
obtenerDatosUsuario(1),
obtenerDatosProducto(42),
obtenerPedidos(1),
];
all($promesas)->then(function (array $resultados) {
[$usuario, $producto, $pedidos] = $resultados;
echo "Todo listo a la vezn";
});
Loop::run();
?>
¿Cuándo usar ReactPHP?
ReactPHP brilla en servidores WebSocket, proxies, scrapers que hacen cientos de peticiones HTTP en paralelo y microservicios con I/O intensivo. No es la mejor opción para aplicaciones web tradicionales donde el coste de inicialización por petición es el cuello de botella; ahí conviene FrankenPHP o Swoole.
