79 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			79 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('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();
 | |
|     }
 | |
| } |