Асинхронное программирование в PHP: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в асинхронное программирование в PHP

Традиционно PHP является синхронным языком: каждый запрос выполняется последовательно, блокируя выполнение до завершения операции ввода-вывода. Однако с ростом требований к производительности веб-приложений, асинхронное программирование в PHP стало необходимостью. Оно позволяет обрабатывать тысячи одновременных соединений, выполнять параллельные задачи и эффективно использовать системные ресурсы.

В этом руководстве мы рассмотрим практические подходы к асинхронному программированию в PHP: от базовых концепций до реальных примеров с использованием современных библиотек и встроенных возможностей PHP 8.1+.

Основы асинхронности: Event Loop и неблокирующий ввод-вывод

В основе любого асинхронного приложения лежит Event Loop (цикл событий). Это бесконечный цикл, который проверяет наличие новых событий (запросов, данных из БД, файлов) и распределяет их обработку без блокировки основного потока.

Синхронный код работает так:

<?php
// Блокирующая операция - программа ждет 5 секунд
sleep(5);
echo "Прошло 5 секунд\n";
// Следующий код выполнится только после sleep
echo "Это сообщение появится через 5 секунд\n";
?>

Асинхронный подход с использованием ReactPHP:

<?php
require 'vendor/autoload.php';

use React\EventLoop\Factory;
use React\Promise\Promise;

$loop = Factory::create();

// Неблокирующий таймер
$loop->addTimer(5, function () {
    echo "Прошло 5 секунд\n";
});

echo "Это сообщение появится сразу\n";
$loop->run();
?>

В асинхронном варианте код не блокируется: сообщение выводится немедленно, а таймер срабатывает в фоне.

Практическое руководство: от простого к сложному

1. Использование Fiber (PHP 8.1+)

Fiber — это встроенный механизм корутин, появившийся в PHP 8.1. Он позволяет приостанавливать выполнение функции и возобновлять его позже без блокировки потока.

Пример параллельного выполнения задач с Fiber:

<?php
function fetchData(string $url): Fiber
{
    return new Fiber(function () use ($url) {
        // Имитация HTTP-запроса
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        
        // Приостанавливаем Fiber
        Fiber::suspend(curl_exec($ch));
        curl_close($ch);
    });
}

// Создаем несколько Fiber
$fiber1 = fetchData('https://api.example.com/data1');
$fiber2 = fetchData('https://api.example.com/data2');

// Запускаем Fiber
$result1 = $fiber1->start();
$result2 = $fiber2->start();

// Возобновляем выполнение
$result1 = $fiber1->resume();
$result2 = $fiber2->resume();

echo "Результат 1: " . $result1 . "\n";
echo "Результат 2: " . $result2 . "\n";
?>

2. Асинхронная работа с БД через Amp

Amp — это мощная библиотека для асинхронного программирования. Рассмотрим пример асинхронного подключения к MySQL:

<?php
require '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);
    
    // Асинхронный запрос - не блокирует выполнение
    $promise1 = $pool->query("SELECT SLEEP(2)");
    $promise2 = $pool->query("SELECT 1 + 1 AS result");
    
    // Оба запроса выполняются параллельно
    $result1 = yield $promise1;
    $result2 = yield $promise2;
    
    foreach ($result2 as $row) {
        echo "Результат: " . $row['result'] . "\n";
    }
    
    $pool->close();
});
?>

3. Создание асинхронного HTTP-сервера на ReactPHP

Полноценный пример асинхронного сервера, обрабатывающего множество соединений:

<?php
require 'vendor/autoload.php';

use React\EventLoop\Factory;
use React\Http\Server;
use React\Http\Response;
use Psr\Http\Message\ServerRequestInterface;

$loop = Factory::create();
$server = new Server($loop, function (ServerRequestInterface $request) {
    $path = $request->getUri()->getPath();
    
    // Асинхронная обработка разных маршрутов
    switch ($path) {
        case '/fast':
            return new Response(200, ['Content-Type' => 'text/plain'], 'Быстрый ответ!');
            
        case '/slow':
            // Имитация медленной операции
            return new Response(200, ['Content-Type' => 'text/plain'], 'Медленный ответ (но не блокирует сервер)');
            
        case '/api/data':
            // Асинхронный запрос к внешнему API
            $deferred = new React\Promise\Deferred();
            
            $loop->addTimer(2, function () use ($deferred) {
                $deferred->resolve(json_encode(['data' => 'async result']));
            });
            
            return $deferred->promise()->then(function ($data) {
                return new Response(200, ['Content-Type' => 'application/json'], $data);
            });
            
        default:
            return new Response(404, [], 'Not Found');
    }
});

$socket = new React\Socket\Server('0.0.0.0:8080', $loop);
$server->listen($socket);

echo "Асинхронный сервер запущен на http://0.0.0.0:8080\n";
$loop->run();
?>

Преимущества и подводные камни асинхронного PHP

Преимущества:

  • Масштабируемость — один процесс может обрабатывать тысячи соединений
  • Эффективность — меньшее потребление памяти по сравнению с thread-based подходами
  • Реальное время — идеально для чатов, уведомлений, стриминга

Подводные камни:

  • Сложность отладки — асинхронный код сложнее отлаживать и тестировать
  • Несовместимость с синх

Блоги

Рекомендаций по книгам