Introduction to Asynchronous Programming in PHP
Traditionally, PHP is a synchronous language: each request is executed sequentially, blocking execution until an I/O operation completes. However, with growing performance demands of web applications, asynchronous programming in PHP has become a necessity. It allows handling thousands of concurrent connections, executing parallel tasks, and efficiently using system resources.
In this guide, we will explore practical approaches to asynchronous programming in PHP: from basic concepts to real-world examples using modern libraries and built-in features of PHP 8.1+.
Fundamentals of Asynchrony: Event Loop and Non-blocking I/O
At the heart of any asynchronous application lies the Event Loop. This is an infinite loop that checks for new events (requests, database data, files) and distributes their processing without blocking the main thread.
Synchronous code works like this:
<?php// Blocking operation - the program waits for 5 secondssleep(5);echo "5 seconds have passed";// The next code will execute only after sleepecho "This message will appear after 5 seconds";?>Asynchronous approach using ReactPHP:
<?phprequire 'vendor/autoload.php';
use React\\EventLoop\\Factory;use React\\Promise\\Promise;
$loop = Factory::create();
// Non-blocking timer$loop->addTimer(5, function () { echo "5 seconds have passed";});
echo "This message will appear immediately";$loop->run();?>In the asynchronous version, the code does not block: the message is displayed immediately, and the timer runs in the background.
Practical Guide: From Simple to Complex
1. Using Fiber (PHP 8.1+)
Fiber is a built-in coroutine mechanism introduced in PHP 8.1. It allows pausing the execution of a function and resuming it later without blocking the thread.
Example of parallel task execution with Fiber:
<?phpfunction fetchData(string $url): Fiber{ return new Fiber(function () use ($url) { // Simulating an HTTP request $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); // Suspend the Fiber Fiber::suspend(curl_exec($ch)); curl_close($ch); });}
// Create multiple Fibers$fiber1 = fetchData('https://api.example.com/data1');$fiber2 = fetchData('https://api.example.com/data2');
// Start the Fibers$result1 = $fiber1->start();$result2 = $fiber2->start();
// Resume execution$result1 = $fiber1->resume();$result2 = $fiber2->resume();
echo "Result 1: " . $result1 . "";echo "Result 2: " . $result2 . "";?>2. Asynchronous Database Work with Amp
Amp is a powerful library for asynchronous programming. Let's look at an example of an asynchronous MySQL connection:
<?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); // Asynchronous query - does not block execution $promise1 = $pool->query("SELECT SLEEP(2)"); $promise2 = $pool->query("SELECT 1 + 1 AS result"); // Both queries execute in parallel $result1 = yield $promise1; $result2 = yield $promise2; foreach ($result2 as $row) { echo "Result: " . $row['result'] . ""; } $pool->close();});?>3. Creating an Asynchronous HTTP Server with ReactPHP
Full-fledged