Introduction à la programmation asynchrone en PHP
Traditionnellement, PHP est un langage synchrone : chaque requête est exécutée séquentiellement, bloquant l'exécution jusqu'à la fin de l'opération d'entrée-sortie. Cependant, avec l'augmentation des exigences de performance des applications web, la programmation asynchrone en PHP est devenue une nécessité. Elle permet de traiter des milliers de connexions simultanées, d'exécuter des tâches parallèles et d'utiliser efficacement les ressources système.
Dans ce guide, nous examinerons les approches pratiques de la programmation asynchrone en PHP : des concepts de base aux exemples concrets utilisant des bibliothèques modernes et les fonctionnalités intégrées de PHP 8.1+.
Fondamentaux de l'asynchrone : Event Loop et entrées-sorties non bloquantes
Au cœur de toute application asynchrone se trouve l'Event Loop (boucle d'événements). Il s'agit d'une boucle infinie qui vérifie la présence de nouveaux événements (requêtes, données de la base de données, fichiers) et répartit leur traitement sans bloquer le thread principal.
Le code synchrone fonctionne ainsi :
<?php// Opération bloquante - le programme attend 5 secondessleep(5);echo "5 secondes se sont écoulées";// Le code suivant ne s'exécutera qu'après sleepecho "Ce message apparaîtra après 5 secondes";?>L'approche asynchrone avec ReactPHP :
<?phprequire 'vendor/autoload.php';
use React\\EventLoop\\Factory;use React\\Promise\\Promise;
$loop = Factory::create();
// Minuterie non bloquante$loop->addTimer(5, function () { echo "5 secondes se sont écoulées";});
echo "Ce message apparaîtra immédiatement";$loop->run();?>Dans la version asynchrone, le code n'est pas bloqué : le message s'affiche immédiatement, tandis que la minuterie se déclenche en arrière-plan.
Guide pratique : du simple au complexe
1. Utilisation de Fiber (PHP 8.1+)
Fiber est un mécanisme intégré de coroutines, apparu dans PHP 8.1. Il permet de suspendre l'exécution d'une fonction et de la reprendre plus tard sans bloquer le thread.
Exemple d'exécution parallèle de tâches avec Fiber :
<?phpfunction fetchData(string $url): Fiber{ return new Fiber(function () use ($url) { // Simulation d'une requête HTTP $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); // Suspension du Fiber Fiber::suspend(curl_exec($ch)); curl_close($ch); });}
// Création de plusieurs Fibers$fiber1 = fetchData('https://api.example.com/data1');$fiber2 = fetchData('https://api.example.com/data2');
// Démarrage des Fibers$result1 = $fiber1->start();$result2 = $fiber2->start();
// Reprise de l'exécution$result1 = $fiber1->resume();$result2 = $fiber2->resume();
echo "Résultat 1 : " . $result1 . "";echo "Résultat 2 : " . $result2 . "";?>2. Travail asynchrone avec une base de données via Amp
Amp est une bibliothèque puissante pour la programmation asynchrone. Voici un exemple de connexion asynchrone à MySQL :
<?phprequire 'vendor/autoload.php';
use Amp\\Loop;use Amp\\Mysql\\ConnectionConfig;use Amp\\Mysql\\Pool;
Loop::run(function () { $config = ConnectionConfig::fromString( "host=localhost user=root password=secret db=test" ); $pool = new Pool($config); // Requête asynchrone - ne bloque pas l'exécution $promise1 = $pool->query("SELECT SLEEP(2)"); $promise2 = $pool->query("SELECT 1 + 1 AS result"); // Les deux requêtes s'exécutent en parallèle $result1 = yield $promise1; $result2 = yield $promise2; foreach ($result2 as $row) { echo "Résultat : " . $row['result'] . ""; } $pool->close();});?>3. Création d'un serveur HTTP asynchrone avec ReactPHP
Complet