ReactPHP (https://github.com/reactphp 29 ) is a set of small packages: on 23 September 2026, composer require react/http react/async installed react/event-loop 1.6.0, react/http 1.11.1, react/promise 3.3.0 and react/async 4.3.0. The global Loop runs when the script ends, and code that must wait returns a promise that settles later:
<?php
require __DIR__ . '/vendor/autoload.php';
use React\EventLoop\Loop;
use React\Http\Message\Response;
$hits = 0;
$http = new React\Http\HttpServer(function ($request) use (&$hits) {
$n = ++$hits;
$ms = (int) ($request->getQueryParams()['ms'] ?? 0);
$reply = Response::plaintext("request $n waited $ms ms, pid " . getmypid() . "\n");
return new React\Promise\Promise( // settle later, from a timer: nothing blocks
fn ($resolve) => Loop::addTimer($ms / 1000, fn () => $resolve($reply)));
});
$http->listen(new React\Socket\SocketServer('127.0.0.1:8223'));
Loop::addPeriodicTimer(0.5, function () use (&$hits) { echo "tick: $hits requests\n"; });php react-server.php & server=$!; sleep 0.3
TIMEFORMAT='all four took %R s'; time seq 4 | xargs -P4 -I{} curl -s "127.0.0.1:8223/?ms=800"
sleep 0.2; kill $serverOutput
tick: 4 requests tick: 4 requests request 1 waited 800 ms, pid 12034 request 2 waited 800 ms, pid 12034 request 3 waited 800 ms, pid 12034 request 4 waited 800 ms, pid 12034 all four took 0.831 s
One PID answered four 800 ms requests in 0.83 s while its timer ticked; PHP-FPM would need four workers. But one sleep() or PDO call stalls every connection, so use async packages such as react/mysql, and run the server under systemd 142,543 (Processes and Monitoring) behind Apache 129 (Apache).