Многие считают PHP исключительно синхронным языком. Но это не так. С появлением современных библиотек и расширений можно писать асинхронный код, который не блокирует выполнение. В этой статье я расскажу, как это работает, покажу примеры и дам практические советы.

Почему асинхронность важна в PHP

Обычный PHP-скрипт работает последовательно: выполнил одну задачу, потом следующую. Если одна из них долгая — например, запрос к базе данных или вызов внешнего API — скрипт ждет. Это тратит время. Асинхронное программирование позволяет запускать такие задачи параллельно. Результат: приложение работает быстрее, пользователь не ждет.

В веб-разработке это особенно полезно для:

  • Обработки нескольких HTTP-запросов
  • Работы с очередями и фоновыми задачами
  • Чтения/записи большого количества файлов

Но как это реализовать в PHP? В отличие от JavaScript или Python, в PHP нет встроенной событийной петли. Однако есть сторонние решения.

Основные инструменты для асинхронности в PHP

В PHP асинхронность достигается через расширения и библиотеки. Вот основные:

ИнструментОписаниеТип
ReactPHPСобытийно-ориентированная библиотека, основанная на циклахБиблиотека
AmpАсинхронный фреймворк с корутинамиБиблиотека
SwooleРасширение на C, добавляющее корутины и серверные возможностиРасширение
pthreadsМногопоточность (устарело, не для современных версий)Расширение

ReactPHP — самый популярный для новичков. Он использует событийный цикл, как в Node.js. Amp — более современный, с поддержкой генераторов. Swoole — мощный, но требует компиляции расширения.

Правило: Начинайте с ReactPHP, если хотите быстро попробовать асинхронность. Он прост в установке через Composer и не требует правок в php.ini.

Пример: асинхронный HTTP-запрос с ReactPHP

Давайте напишем простой скрипт, который отправляет несколько запросов параллельно. Сначала установим библиотеку:

composer require react/http

Теперь код. Он создает несколько запросов и запускает их одновременно:

<?php

require __DIR__ . '/vendor/autoload.php';

use React\EventLoop\Factory;
use React\HttpClient\Client;

$loop = Factory::create();
$client = new Client($loop);

$urls = [
    'https://api.example.com/data1',
    'https://api.example.com/data2',
    'https://api.example.com/data3',
];

foreach ($urls as $url) {
    $request = $client->request('GET', $url);
    $request->on('response', function ($response) use ($url) {
        $response->on('data', function ($chunk) use ($url) {
            echo "Получены данные от $url: " . strlen($chunk) . " байт\n";
        });
    });
    $request->end();
}

$loop->run();
?>

Что здесь происходит? Мы создаем событийный цикл ($loop). Добавляем три запроса. Каждый обрабатывается асинхронно: когда приходит ответ, вызывается callback. Цикл работает, пока все запросы не завершатся. В итоге запросы выполняются параллельно, а не последовательно.

Корутины в Amp: еще проще

Amp использует генераторы PHP для создания корутин. Это выглядит как синхронный код, но работает асинхронно. Пример:

<?php

require __DIR__ . '/vendor/autoload.php';

use Amp\Loop;
use Amp\Http\Client\HttpClientBuilder;

Loop::run(function () {
    $client = HttpClientBuilder::buildDefault();
    
    $promises = [
        $client->request('https://api.example.com/data1'),
        $client->request('https://api.example.com/data2'),
    ];
    
    $responses = yield Amp\Promise\all($promises);
    
    foreach ($responses as $response) {
        $body = yield $response->getBody()->buffer();
        echo "Длина ответа: " . strlen($body) . "\n";
    }
});
?>

Ключевое слово yield приостанавливает выполнение до завершения промиса. Это делает код читаемым, как в синхронном стиле. Amp автоматически управляет циклом событий.

Swoole: асинхронность на уровне расширения

Swoole — это расширение для PHP, написанное на C. Оно добавляет корутины и позволяет создавать высокопроизводительные серверы. Пример корутины в Swoole:

<?php

Co\run(function () {
    $result1 = go(function () {
        $cli = new Swoole\Coroutine\Http\Client('api.example.com', 443, true);
        $cli->get('/data1');
        return $cli->body;
    });
    
    $result2 = go(function () {
        $cli = new Swoole\Coroutine\Http\Client('api.example.com', 443, true);
        $cli->get('/data2');
        return $cli->body;
    });
    
    echo "Результат 1: " . strlen($result1) . "\n";
    echo "Результат 2: " . strlen($result2) . "\n";
});
?>

Swoole автоматически переключает контекст при вызовах ввода-вывода. Это очень быстро, но требует установки расширения через pecl. Не на всех хостингах это доступно.

Лайфхак: Если вы используете Laravel или Symfony, обратите внимание на пакет spatie/async. Он упрощает асинхронный запуск задач в PHP без глубокого изучения событийных циклов.

Когда асинхронность не нужна

Не стоит внедрять асинхронность везде. Если ваш скрипт делает один запрос к БД и отдает HTML — синхронность вполне подходит. Асинхронность оправдана, когда:

  • Есть много независимых операций ввода-вывода
  • Нужно обрабатывать тысячи одновременных соединений (например, веб-сокеты)
  • Вы пишете долго работающий процесс (например, парсер или чат-бот)

Помните: асинхронный код сложнее отлаживать. Убедитесь, что вы готовы к этому.

Что в итоге

PHP — не такой уж и синхронный язык. ReactPHP, Amp и Swoole дают возможность писать асинхронный код, который ускоряет работу приложений. Начинайте с ReactPHP для простых задач, переходите на Amp для более сложных сценариев. Swoole — для максимальной производительности, если есть возможность установить расширение. Асинхронность — это мощный инструмент, но используйте его с умом.

Студия WNDER