Search code examples
yii2server-sent-events

How to stream output for SSE in yii2


i want to send "server sent events" SSE from my yii2 application. when i try this script (without yii2), line after line is being print immediately, each after a one-second pause.

sse.php

<?php

date_default_timezone_set("Europe/Vienna");
header("Content-Type: text/event-stream");
header('X-Accel-Buffering: no');


$i = 0;

while ($i++ < 3) {
    $curDate = date(DATE_ISO8601);
    echo 'data: ' . json_encode(['message' => $curDate]), "\n\n";


    while (ob_get_level() > 0) {
        ob_end_flush();
    }
    flush();


    if (connection_aborted()) {
        break;
    }

    sleep(1);
}

when i try this action (extending yii\base\Controller), the output is being buffered and sent as once, after 3 seconds.

SseController.php

<?php

namespace frontend\controllers;

use Yii;

class SseController extends \yii\base\Controller
{
    public function actionStream()
    {
        $this->layout = false;

        Yii::$app->response->format = \yii\web\Response::FORMAT_RAW;
        Yii::$app->response->isSent = true;
        Yii::$app->session->close();

        header('Content-Type: text/event-stream');
        header("Cache-Control: no-cache, must-revalidate");
        header("X-Accel-Buffering: no");


        $i = 0;

        while ($i++ < 3) {
            $curDate = date(DATE_ISO8601);
            echo 'data: ' . json_encode(['message' => $curDate]), "\n\n";

            while (ob_get_level() > 0) {
                ob_end_flush();
            }
            flush();


            if (connection_aborted()) {
                break;
            }

            sleep(1);
        }

        Yii::$app->response->statusCode = 404;
        Yii::$app->response->data = null;
    }
}

So, how can i configure yii's controller to dump raw output immediately, without buffering, to make sse work? sse.php test script is meant to prove that server settings are ok.


Solution

  • If the problem is only in the Yii, then perhaps this additional will help:

    Yii::$app->response->stream = true;
    

    However, both versions of your code worked fine for me locally with Yii2, but did not work on the hosting. This additional helped me:

    @ob_end_clean();
    ini_set('output_buffering', 0);
    

    Here is the full code of the method if anyone needs it:

    public function yourAction() {
        @ob_end_clean();
        ini_set('output_buffering', 0);
        
        header('Content-Type: text/event-stream');
        header("Cache-Control: no-cache, must-revalidate");
        header("X-Accel-Buffering: no");
        
        echo "data: 1\n\n";
        flush();
        
        sleep(1);
        
        echo "data: 2\n\n";
        flush();
    }