Server-Sent Events в PHP: реалтайм без WebSocket и головной боли

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

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

Начать курс

Server-Sent Events в PHP: реалтайм, который не кусается

Вы когда-нибудь задумывались, почему ваш любимый мессенджер или биржа криптовалют обновляют данные мгновенно, а ваш сайт на PHP всё ещё заставляет пользователя жать F5? Кажется, что реалтайм — это удел избранных, вооружённых Node.js или дорогими WebSocket-серверами. Но что, если я скажу вам, что Server-Sent Events (SSE) в PHP — это простой, элегантный и невероятно эффективный способ добавить живые обновления на ваш проект?

Добро пожаловать на PythonLib, где мы ломаем стереотипы. Сегодня мы разберём Server-Sent Events PHP — технологию, которая позволяет серверу «толкать» данные клиенту через обычный HTTP, без единой строчки JavaScript-магии (ну, почти без неё). Никаких сложных протоколов, никаких проблем с NAT и прокси — только чистый, понятный PHP и поток данных, который течёт, как горная река.

Что такое Server-Sent Events и почему это «серебряная пуля» для PHP?

SSE — это спецификация HTML5, которая позволяет серверу отправлять клиенту произвольное количество событий через одно долгоживущее HTTP-соединение. В отличие от WebSocket, который двунаправленный, SSE работает только «от сервера к клиенту». И это прекрасно! Для 90% задач (уведомления, ленты новостей, мониторинг, биржевые тикеры) обратная связь не нужна. Зачем тащить тяжелую артиллерию, когда можно обойтись лёгким пехотным оружием?

Главные плюсы реалтайм уведомлений PHP через SSE:

  • Простота реализации — на стороне сервера это обычный PHP-скрипт, который не закрывает соединение.
  • Автоматическое восстановление — браузер сам переподключается, если соединение оборвалось.
  • Работает через любой прокси — HTTP/1.1, HTTP/2, Nginx, Apache — всё из коробки.
  • Низкое потребление ресурсов — в отличие от polling, запросы не дублируются каждые 2 секунды.

Конечно, у SSE есть ограничения: максимальное количество одновременных соединений в браузере (6 на домен), и они не поддерживаются в старых Internet Explorer. Но для современных приложений это не проблема. А если нужно больше — используйте HTTP/2, где эти ограничения снимаются.

Как работает Server-Sent Events на PHP: от теории к практике

Давайте представим, что ваш PHP-скрипт — это бармен. Обычный запрос — это когда клиент подходит, заказывает напиток (делает GET-запрос), бармен наливает (выполняет код), отдаёт стакан (отправляет ответ) и закрывает бар. При SSE клиент подходит, заказывает «пожалуйста, наливайте мне новый коктейль каждые 5 секунд», и бармен начинает лить бесконечную струю, не закрывая кран.

Технически это выглядит так:

  1. Клиент (браузер) создаёт объект EventSource и отправляет GET-запрос на PHP-скрипт.
  2. Сервер устанавливает заголовок Content-Type: text/event-stream и отключает кэширование.
  3. Сервер входит в бесконечный цикл, отправляя данные в формате data: {"message": "hello"}\n\n.
  4. Когда серверу нужно остановиться (или клиент закрыл вкладку), соединение разрывается.

Первый пример: простое «здравствуй, мир» на SSE в PHP

Создадим файл sse.php. Это будет наш «вечный двигатель»:

<?php
// sse.php - простейший сервер Server-Sent Events
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // для Nginx

// Убедимся, что PHP не обрывает выполнение
set_time_limit(0);

$counter = 0;

while (true) {
    $counter++;
    $data = ['time' => date('H:i:s'), 'count' => $counter];
    
    // Отправляем событие
    echo "id: {$counter}\n";
    echo "event: update\n";
    echo "data: " . json_encode($data) . "\n\n";
    
    // Принудительно отправляем буфер
    ob_flush();
    flush();
    
    // Проверяем, не оборвалось ли соединение
    if (connection_aborted()) {
        break;
    }
    
    // Ждём 1 секунду
    sleep(1);
}
?>

А на клиенте (HTML + JavaScript) подключаемся к этому потоку:

<!DOCTYPE html>
<html>
<head>
    <title>SSE Demo | PythonLib</title>
</head>
<body>
    <h2>Живое время с сервера</h2>
    <div id="output"></div>
    
    <script>
        const source = new EventSource('sse.php');
        
        source.addEventListener('update', function(event) {
            const data = JSON.parse(event.data);
            document.getElementById('output').innerHTML = 
                `Обновление #${data.count}: ${data.time}`;
        });
        
        source.onerror = function() {
            console.log('Ошибка соединения, переподключаемся...');
        };
    </script>
</body>
</html>

Запустите это в браузере — и вы увидите, как время обновляется каждую секунду. Это и есть push-уведомления PHP в действии. Просто? Чертовски просто!

Продвинутый пример: отправляем уведомления из базы данных

Теперь сделаем что-то полезное. Допустим, у нас есть таблица notifications в MySQL, и мы хотим в реальном времени показывать пользователю новые записи. Вместо того чтобы гонять AJAX каждые 2 секунды, используем SSE.

Серверный скрипт sse_notifications.php:

<?php
// sse_notifications.php - поток уведомлений из БД
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no');

set_time_limit(0);

// Подключение к БД (PDO)
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8';
$pdo = new PDO($dsn, 'user', 'password');

$lastId = 0;

while (true) {
    // Получаем новые записи, начиная с последнего ID
    $stmt = $pdo->prepare("SELECT id, message, created_at FROM notifications WHERE id > ? ORDER BY id ASC LIMIT 10");
    $stmt->execute([$lastId]);
    $notifications = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    foreach ($notifications as $notif) {
        echo "id: {$notif['id']}\n";
        echo "event: new_notification\n";
        echo                

Блоги

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