Boucles d’événements avec ReactPHP
Exécutez des entrées-sorties non bloquantes avec la boucle d’événements ReactPHP.
Boucles d’événements avec ReactPHP est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.
ReactPHP en une phrase
ReactPHP est une boîte à outils entièrement écrite en PHP et sans dépendances, destinée aux E/S non bloquantes et construite autour d’une seule boucle d’événements. Aucune extension n’est requise : elle fonctionne avec le PHP standard grâce à stream_select (ou à ext-event/ext-ev lorsqu’elles sont disponibles). Elle permet d’écrire en PHP standard des serveurs à longue durée de vie, des clients HTTP concurrents et des chaînes de traitement en flux.
La boucle globale
Les versions modernes de ReactPHP (v1.2+) exposent une boucle par défaut grâce à la façade statique React\EventLoop\Loop ; il n’est plus nécessaire de transmettre une instance de boucle partout. Planifiez une fonction de rappel avec une minuterie ; la boucle s’exécute jusqu’à ce qu’il n’y ait plus de travail.
<?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+Minuteries et tâches périodiques
La gestion non bloquante du temps remplace sleep(). addTimer se déclenche une fois ; addPeriodicTimer se répète. Annulez l’une ou l’autre pour l’arrêter. Comme ces opérations sont pilotées par la boucle, les autres tâches continuent de s’exécuter pendant l’attente de la minuterie.
<?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
}
});Promesses pour les résultats asynchrones
ReactPHP renvoie une promesse pour toute opération qui se termine plus tard. Attachez then() pour gérer la réussite et une seconde fonction de rappel pour l’échec. Les promesses se composent ; vous pouvez donc transformer et enchaîner les résultats sans imbriquer profondément les fonctions de rappel.
<?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 concurrent avec le client asynchrone
Le grand avantage : lancer de nombreuses requêtes HTTP de manière concurrente depuis un seul fil d’exécution. React\Http\Browser renvoie des promesses ; React\Promise\all() attend l’ensemble du lot. Dix requêtes d’une seconde s’achèvent en environ une seconde, et non en dix.
<?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";
});Les Fibres donnent une apparence synchrone au code
Les chaînes de fonctions de rappel deviennent difficiles à gérer. ReactPHP s’intègre à react/async, qui utilise les Fibres de PHP 8.1 pour vous permettre d’attendre une promesse avec await() et d’écrire du code linéaire. L’enveloppe coroutine()/async() exécute votre fonction à l’intérieur d’une fibre.
<?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 serveur TCP non bloquant
ReactPHP excelle pour les serveurs. Un SocketServer accepte les connexions et émet des événements ; chaque connexion est elle-même un flux non bloquant. Un seul processus gère des milliers de clients simultanés.
<?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 serveur HTTP en quelques lignes
Le serveur react/http traite les requêtes avec une fonction de rappel qui renvoie une Response (ou une promesse qui en contient une). Comme il s’agit d’un processus persistant, vous initialisez les dépendances une seule fois — mais vous devez aussi gérer la mémoire entre les requêtes.
<?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";Diffusion en continu sans mise en mémoire tampon
Les flux de ReactPHP appliquent une contre-pression : vous pouvez acheminer pipe() un flux lisible vers un flux inscriptible, et l’environnement d’exécution régule automatiquement le débit. Vous pouvez ainsi transférer des gigaoctets avec une empreinte mémoire faible et constante, au lieu de charger des fichiers dans 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 fileLe péché capital : bloquer la boucle
Tout se brise dès que vous appelez une fonction bloquante dans un gestionnaire — sleep(), PDO bloquant, curl_exec, file_get_contents sur une URL. Ces fonctions figent le fil d’exécution unique et bloquent toutes les connexions. Utilisez les équivalents React : Loop::addTimer pour les délais, clue/reactphp-* ou des pilotes de base de données asynchrones pour les requêtes, et Browser pour 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/coroutineLimiter la concurrence
Une multiplication sans limite peut épuiser les descripteurs de fichiers ou surcharger une API en aval. clue/mq-react de ReactPHP met les tâches en file d’attente avec une limite de concurrence : il traite N promesses à la fois pendant que les autres attendent. Cela impose une contre-pression à votre propre charge sortante.
<?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";
});Vérification rapide
Comment attendre 2 secondes dans un gestionnaire ReactPHP sans nuire à la concurrence ?
Récapitulatif
ReactPHP apporte les entrées-sorties non bloquantes à PHP standard :
- Une seule boucle d'événements (façade
Loop) pilote les minuteurs, les points de connexion et les flux : aucune extension n'est nécessaire. - Les opérations renvoient des promesses ;
all()exécute les requêtes en parallèle, etreact/asyncainsi que les fibres vous permettent d'utiliserawait()dans un code d'apparence synchrone. - Vous pouvez créer des serveurs TCP/HTTP persistants et des flux avec contrôle de la contre-pression capables de gérer des milliers de connexions par processus.
- Ne bloquez jamais la boucle : remplacez
sleepet les entrées-sorties bloquantes par les minuteurs de React et des pilotes asynchrones.
Questions Fréquemment Posées
La leçon « Boucles d’événements avec ReactPHP » est-elle gratuite ?
Oui — le texte complet de « Boucles d’événements avec ReactPHP » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Boucles d’événements avec ReactPHP » ?
Exécutez des entrées-sorties non bloquantes avec la boucle d’événements ReactPHP. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer PHP Academy ?
Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « Boucles d’événements avec ReactPHP » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?
Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Modèle de concurrence de PHP
- Fibers de PHP 8.1
- Boucles d’événements avec ReactPHP
- Serveurs hautes performances avec Swoole