This repository has been archived on 2023-01-06. You can view files and clone it, but cannot push or open issues or pull requests.
web/app/lib/App/SSE/EventLoop.php
2022-03-28 08:46:02 +00:00

80 lines
2.3 KiB
PHP

<?php
namespace App\SSE;
/**
* Small class to abstract Server-Sent Events (SSE)
*
* https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
*/
class EventLoop
{
public int $interval = 3;
public int $heartbeat = 5; // send heartbeat every num seconds to ensure connection is still alive
public int $timeLimit = 3600;
public int $execLimit = 30;
public function start(callable $callback): void
{
// as session data is locked to prevent concurrent writes we
// make it read only to prevent the server from locking up
if (session_status() === PHP_SESSION_ACTIVE)
{
session_write_close();
}
header('Cache-Control: no-store, must-revalidate');
header('Content-Type: text/event-stream');
// explicitly disable caching so varnish and other upstreams won't cache.
header('Cache-Control: no-cache, must-revalidate');
// instruct nginx to disable fastcgi_buffering and disable gzip
header('X-Accel-Buffering: no');
// send headers to client indicating we are now a stream
ob_end_flush();
flush();
$lastHeartbeat = time();
while (!connection_aborted() && time() < (time() + $this->timeLimit))
{
set_time_limit($this->execLimit);
try {
$data = call_user_func($callback);
if ($data !== NULL)
{
$this->send($data);
$lastHeartbeat = time();
}
} catch (StopEventLoopException $th) {
break;
}
// sleep and perform heartbeat to ensure connection is still alive
for ($i = 0; $i < $this->interval; $i++)
{
if (time() >= $lastHeartbeat + $this->heartbeat)
{
echo ": \n\n";
ob_end_flush();
flush();
$lastHeartbeat = time();
}
sleep(1);
}
}
}
/**
* Send data to client encoded as json
*/
private function send($data): void
{
echo "data: " . json_encode($data);
echo "\n\n";
// send data to stream
ob_end_flush();
flush();
}
}