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
// Логируем ошибки