Server-Sent Events в PHP: как отправлять данные с сервера в реальном времени

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

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

Начать курс

Server-Sent Events в PHP: когда сервер сам стучится в браузер

Вы когда-нибудь задумывались, как работают ленты новостей в Twitter, курсы валют на бирже или уведомления в мессенджере? Правильно — данные приходят сами, без обновления страницы и без бесконечных Ajax-запросов. Большинство разработчиков сразу вспоминают WebSockets, но есть и другой, гораздо более простой и элегантный способ — Server-Sent Events (SSE).

В этой статье мы разберем, как реализовать Server-Sent Events в PHP с нуля. Вы узнаете, как отправлять данные с сервера в реальном времени, не нагружая инфраструктуру сложными библиотеками. Никакого Node.js, никаких очередей — только чистый PHP и HTML5.

Что такое Server-Sent Events и зачем они нужны?

SSE (Server-Sent Events) — это стандарт HTML5, который позволяет серверу отправлять данные клиенту по одному HTTP-соединению. В отличие от WebSockets, которые работают в обе стороны, SSE — это односторонний канал: данные идут только от сервера к клиенту. Но именно в этом и кроется его главное преимущество: реализация настолько проста, что для нее не нужны ни дополнительные библиотеки, ни специальные серверы.

Где применяются server-sent events php?

  • Ленты новостей и уведомления в реальном времени
  • Мониторинг серверов и логов
  • Курсы валют, котировки акций
  • Индикаторы прогресса длительных операций (загрузка файлов, обработка видео)
  • Чат-боты и системы оповещений

В отличие от классического polling (когда клиент каждые N секунд дергает сервер), SSE PHP создает постоянное соединение, которое не требует повторных рукопожатий и заголовков. Это экономит трафик и ресурсы CPU.

Как работает SSE: протокол и заголовки

В основе SSE лежит обычный HTTP-ответ с особым типом контента. Сервер отправляет данные в текстовом формате, используя ключевые слова event, data, id и retry. Клиент получает поток и обрабатывает события по мере поступления.

Базовый протокол выглядит так:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

data: Привет, мир

data: {"message": "Это JSON"}

Каждое сообщение начинается с data:, после чего идет текст. Если нужно отправить несколько строк — просто пишем несколько data: подряд. Пустая строка \n\n сигнализирует о конце сообщения.

Пишем первый SSE-скрипт на PHP

Давайте создадим простой скрипт, который будет отправлять текущее время каждые 2 секунды. Это классический пример, который сразу показывает магию реального времени PHP.

Файл sse.php:

<?php
// Отключаем буферизацию вывода
ob_implicit_flush(true);
ob_end_flush();

// Устанавливаем заголовки SSE
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // Для Nginx

while (true) {
    // Формируем данные
    $data = [
        'time' => date('H:i:s'),
        'timestamp' => time()
    ];
    
    // Отправляем событие
    echo "data: " . json_encode($data) . "\n\n";
    
    // Проверяем, не закрыл ли клиент соединение
    if (connection_aborted()) {
        break;
    }
    
    // Пауза 2 секунды
    sleep(2);
}
?>

Обратите внимание: мы используем бесконечный цикл while(true), но обязательно проверяем connection_aborted(). Если клиент закрыл вкладку, скрипт завершится, не нагружая сервер.

Теперь напишем клиентскую часть на JavaScript:

<!DOCTYPE html>
<html>
<head>
    <title>SSE Demo PHP</title>
</head>
<body>
    <h2>Текущее время с сервера:</h2>
    <div id="time">Ожидание...</div>
    
    <script>
        const eventSource = new EventSource('sse.php');
        
        eventSource.onmessage = function(event) {
            const data = JSON.parse(event.data);
            document.getElementById('time').innerHTML = 
                'Время: ' + data.time + '<br>' +
                'Timestamp: ' + data.timestamp;
        };
        
        eventSource.onerror = function() {
            console.error('Ошибка соединения SSE');
        };
    </script>
</body>
</html>

Запустите оба файла на локальном сервере — и вы увидите, как время обновляется каждые 2 секунды без перезагрузки страницы. Это и есть server sent events php в действии.

Продвинутые техники: именованные события и переподключение

Протокол SSE поддерживает именованные события. Это позволяет отправлять разные типы данных в одном потоке. Например, уведомления и обновления статуса:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$counter = 0;

while (true) {
    $counter++;
    
    // Именованное событие "notification"
    if ($counter % 3 == 0) {
        echo "event: notification\n";
        echo "data: " . json_encode(['text' => 'Новое уведомление #' . $counter]) . "\n\n";
    }
    
    // Именованное событие "update"
    echo "event: update\n";
    echo "data: " . json_encode(['progress' => $counter * 10]) . "\n\n";
    
    if (connection_aborted()) break;
    sleep(1);
}
?>

Клиент будет обрабатывать их отдельно:

const eventSource = new EventSource('events.php');

eventSource.addEventListener('notification', function(e) {
    const data = JSON.parse(e.data);
    showNotification(data.text);
});

eventSource.addEventListener('update', function(e) {
    const data = JSON.parse(e.data);
    updateProgress(data.progress);
});

Также вы можете влиять на поведение переподключения. По умолчанию, если соединение разрывается, браузер пытается восстановить его через 3 секунды. Вы можете изменить этот интервал, отправив retry: 5000 (в миллисекундах).

Обработка ошибок и переподключение в PHP SSE

Одна из главных проблем SSE PHP — это длительность выполнения скрипта. По умолчанию PHP убивает скрипты через 30 секунд. Чтобы этого избежать, нужно явно указать таймаут:

<?php
set_time_limit(0); // Бесконечное выполнение
ini_set('max_execution_time', 0);

// ... остальной код

Для продакшена обязательно добавьте логирование ошибок и обработку сбоев:

<?php
// Логируем ошибки                

Блоги

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