Skip to content
Advertisement

While loops for server-sent events are causing page to freeze

I am currently working on a chat that uses Server-Sent Events to receive the messages. However, I am running into a problem. The server-sent event never connects and stays at pending because the page doesn’t load.

For example:

<?php
    while(true) {
        echo "data: This is the message.";
        sleep(3);
        ob_flush();
        flush();
    }
?>

I expect that every 3 seconds, “data: This is the message.” will be outputted. Instead, the page just doesn’t load. However, I need this behavior for server-sent events. Is there a way to fix this?

Edit:

Full Code:

<?php
   session_start();

    require "connect.php";
    require "user.php";

    session_write_close();

    echo $data["number"];

    header("Content-Type: text/event-streamnn");
    header('Cache-Control: no-cache');

    set_time_limit(1200);

    $store = new StdClass(); // STORE LATEST MESSAGES TO COMPARE TO NEW ONES
    $ms = 200; // REFRESH TIMING (in ms)
    $go = true; // MESSAGE CHANGED

    function formateNumber ($n) {
            $areaCode = substr($n, 0, 3);
            $part1 = substr($n, 3, 3);
            $part2 = substr($n, 6, 4);
            return "($areaCode) $part1-$part2";
    }

    function shorten ($str, $mLen, $elp) {
        if (strlen($str) <= $mLen) { 
            return $str;
        } else {
            return rtrim(substr($str, 0, $mLen)) . $elp;
        }
    }

   do {
    $number = $data["number"];
        $sidebarQ = "
            SELECT * 
            FROM (
                SELECT * 
                FROM messages 
                WHERE deleted NOT LIKE '%$number%' 
                AND (
                    `from`='$number' 
                    OR 
                    `to`='$number'
                ) 
                ORDER BY `timestamp` DESC
            ) as mess 
            GROUP BY `id` 
            ORDER BY `timestamp` DESC";
        $query = $mysqli->query($sidebarQ);

        if ($query->num_rows == 0) {
            echo 'data: null' . $number;
            echo "nn";
        } else {

            $qr = array();
            while($row = $query->fetch_assoc()) {
                $qr[] = $row;
            }

            foreach ($qr as $c) {
                $id = $c["id"];
                if (!isset($store->{$id})) {
                    $store->{$id} = $c["messageId"];
                    $go = true;
                } else {
                    if ($store->{$id} != $c["messageId"]) {
                        $go = true;
                        $store->{$id} = $c["messageId"];
                    }
                }
            }

            if($go == true) {
                $el = $n = "";

                foreach ($qr as $rows) {
                    $to = $rows["to"];
                    $id = $rows["id"];
                    $choose = $to == $number ? $rows["from"] : $to;
                    $nameQuery = $mysqli->query("SELECT `savedname` FROM `contacts` WHERE `friend`='$choose' AND `number`='$number'");
                    $nameGet = $nameQuery->fetch_assoc();
                    $hasName = $nameQuery->num_rows == 0 ? formateNumber($choose) : $nameGet["savedname"];

                    $new = $mysqli->query("SELECT `id` FROM `messages` WHERE `to`='$number' AND `tostatus`='0' AND `id`='$id'")->num_rows;
                    if ($new > 0) {
                        $n = "<span class='new'>" . $new . "</span>";
                    }

                    $side = "<span style='color:#222'>" . ($to == $number ? "To you:" : "From you:") . "</span>";
                    $el .= "<div class='messageBox sBox" . ($nameQuery->num_rows == 0 ? " noname" : "") . "' onclick="GLOBAL.load($id, $choose)" data-id='$id'><name>$hasName</name><div>$side " . shorten($rows["message"], 25, "...") . "</div>$n</div>";
                }
                echo 'data: '. $el;
                echo "nn";

                $go = false;
            }
        }

        echo " ";

        ob_flush();
        flush();
        sleep(2);
    } while(true);
?>

I would also like to note, that this infinite loop shouldn’t be causing this to happen. This is just how SSE’s are set up usually and it is even done so on the MDN website.

Advertisement

Answer

No doubt by now you have figured this out but on the offchance you have not I used code like the following on a couple of sse scripts and it worked like a charm. The code below is generic and does not feature your sql or recordset processing but the idea is sound(!?)

<?php
    set_time_limit( 0 );
    ini_set('auto_detect_line_endings', 1);
    ini_set('mysql.connect_timeout','7200');
    ini_set('max_execution_time', '0');

    date_default_timezone_set( 'Europe/London' );
    ob_end_clean();
    gc_enable();



    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    header('Access-Control-Allow-Credentials: true');
    header('Access-Control-Allow-Methods: GET');
    header('Access-Control-Expose-Headers: X-Events');  




    if( !function_exists('sse_message') ){
        function sse_message( $evtname='chat', $data=null, $retry=1000 ){
            if( !is_null( $data ) ){
                echo "event:".$evtname."rn";
                echo "retry:".$retry."rn";
                echo "data:" . json_encode( $data, JSON_FORCE_OBJECT|JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS );
                echo "rnrn";    
            }
        }
    }

    $sleep=1;
    $c=1;

   $pdo=new dbpdo();/* wrapper class for PDO that simplifies using PDO */

    while( true ){
        if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
            break;
        }
        /* Infinite loop is running - perform actions you need */

        /* Query database */
        /*
            $sql='select * from `table`';
            $res=$pdo->query($sql);
        */

        /* Process recordset from db */
        /*
        $payload=array();
        foreach( $res as $rs ){
            $payload[]=array('message'=>$rs->message);  
        }
        */

        /* prepare sse message */
        sse_message( 'chat', array('field'=>'blah blah blah','id'=>'XYZ','payload'=>$payload ) );

        /* Send output */
        if( @ob_get_level() > 0 ) for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
        @flush();

        /* wait */
        sleep( $sleep );
        $c++;

        if( $c % 1000 == 0 ){/* I used this whilst streaming twitter data to try to reduce memory leaks */
            gc_collect_cycles();
            $c=1;   
        }
    }



    if( @ob_get_level() > 0 ) {
        for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
        @ob_end_clean();
    }
?>
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement