Введение в асинхронное программирование в PHP
Традиционно PHP работает в синхронном режиме: каждый запрос выполняется последовательно, строка за строкой. Однако в современной веб-разработке всё чаще требуется обрабатывать множество задач одновременно — от отправки писем до работы с WebSocket и API. Асинхронное программирование в PHP позволяет решать эти задачи эффективно, не блокируя основной поток выполнения.
В этом руководстве мы разберём, что такое асинхронность, зачем она нужна, и как начать её использовать в PHP. Вы узнаете о ключевых инструментах: Fiber, ReactPHP, Swoole и встроенных функциях для работы с корутинами.
Что такое асинхронное программирование и зачем оно нужно?
Синхронный vs Асинхронный код
Представьте, что вы заказываете пиццу. Синхронный подход: вы звоните, ждёте, пока её приготовят, и только потом кладёте трубку. Асинхронный: вы звоните, оставляете заказ, а пока пиццу готовят — занимаетесь своими делами. В PHP это выглядит так:
// Синхронный код (блокирующий)
$data = file_get_contents('https://api.example.com');
echo "Данные получены: " . $data; // Ждём, пока загрузится
// Асинхронный код (неблокирующий)
$promise = async_file_get_contents('https://api.example.com');
echo "Запрос отправлен, можно делать другие дела...";
$data = await($promise); // Получаем результат позже
Проблемы синхронного PHP
- Блокировка I/O: операции с файлами, БД или сетевыми запросами останавливают выполнение скрипта
- Низкая производительность: каждый новый запрос требует отдельного процесса или потока
- Проблемы с реальным временем: чаты, уведомления и стриминг работают неэффективно
Преимущества асинхронного подхода
- Одновременная обработка сотен и тысяч соединений
- Экономия памяти и ресурсов сервера
- Возможность создавать real-time приложения
- Более отзывчивые интерфейсы
Основные инструменты асинхронного PHP
1. Корутины и Fiber (PHP 8.1+)
Начиная с PHP 8.1, появилась встроенная поддержка Fiber — легковесных корутин. Fiber позволяет приостанавливать выполнение функции и возобновлять её позже, не блокируя основной поток.
<?php
$fiber = new Fiber(function(): void {
$value = Fiber::suspend('first');
echo "Возобновлён со значением: $value\n";
});
$value = $fiber->start();
echo "Начальное значение: $value\n"; // first
$fiber->resume('second'); // Возобновлён со значением: second
?>
2. ReactPHP — Event Loop для PHP
ReactPHP — это библиотека, которая реализует событийно-ориентированное программирование (event loop). Она позволяет выполнять неблокирующие операции ввода-вывода.
<?php
require 'vendor/autoload.php';
$loop = React\EventLoop\Loop::get();
$loop->addTimer(1.0, function () {
echo "Прошла 1 секунда\n";
});
$loop->addTimer(2.0, function () {
echo "Прошло 2 секунды\n";
});
echo "Старт...\n";
$loop->run();
?>
ReactPHP также предоставляет асинхронные HTTP-клиенты и серверы:
<?php
$browser = new React\Http\Browser($loop);
$browser->get('https://api.example.com')->then(
function (Psr\Http\Message\ResponseInterface $response) {
echo "Ответ: " . $response->getBody();
},
function (Exception $e) {
echo "Ошибка: " . $e->getMessage();
}
);
?>
3. Swoole — высокопроизводительный асинхронный сервер
Swoole — это расширение для PHP, которое превращает его в полноценный асинхронный сервер. Оно поддерживает корутины, WebSocket, TCP/UDP серверы и многое другое.
<?php
// Простой HTTP-сервер на Swoole
$http = new Swoole\Http\Server("0.0.0.0", 9501);
$http->on("request", function ($request, $response) {
$response->header("Content-Type", "text/plain");
$response->end("Hello World\n");
});
$http->start();
?>
С корутинами Swoole код выглядит почти как синхронный, но работает асинхронно:
<?php
go(function () {
// Асинхронный HTTP-запрос
$data = Co::get('https://api.example.com');
echo "Данные: " . $data;
});
?>
Практические примеры асинхронного кода
Пример 1: Асинхронная отправка email
Вместо того чтобы заставлять пользователя ждать, пока письмо отправится, используем ReactPHP:
<?php
use React\EventLoop\Loop;
use React\Promise\Deferred;
function sendEmailAsync(string $to, string $subject, string $body): React\Promise\PromiseInterface
{
$deferred = new Deferred();
// Имитация отправки через 2 секунды
Loop::get()->addTimer(2.0, function () use ($deferred, $to) {
echo "Письмо отправлено на $to\n";
$deferred->resolve(true);
});
return $deferred->promise();
}
// Использование
echo "Начинаем отправку...\n";
sendEmailAsync('user@example.com', 'Привет', 'Тело письма')->then(function () {
echo "Отправка завершена!\n";
});
echo "Пользователь может продолжать работу...\n";
?>
Пример 2: Параллельный парсинг нескольких сайтов
<?php
require 'vendor/autoload.php';
use React\Http\Browser;
use React\EventLoop\Loop;
$loop = Loop::get();
$browser = new Browser($loop);
$urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com