How to increase Server-Sent Event's reopen time? - php

I'm working to show notifications from Server-Sent Event. I checked that every time the browser tries to reconnect with the source about 3 seconds after each connection is closed. That event is getting a call too fast, so my server is loaded too.
So how do I change the reopening time to increase? I have to do at least 60 seconds, so tell me how to do it?
I'm trying the following code.
<table class="table" id="notification"></table>
<script type="text/javascript">
var ssevent = null;
if (!!window.EventSource) {
ssevent = new EventSource('ssevent.php');
ssevent.addEventListener('message', function(e){
if(e.data){
json = JSON.parse(e.data);
if(json){
json.forEach(function(v,i){
html = "<tr><td>"+ v.text +"</td><td>"+ v.date +"</td></tr>";
});
$('#notification').append(html);
}
}
}, false);
ssevent.addEventListener('error', function(e) {
if (e.readyState == EventSource.CLOSED){
console.log("Connection was closed.");
}
}, false);
} else {
console.log('Server-Sent Events not support in your browser');
}
</script>
The file of event stream is as follow.
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
include_once "config.php";
$time = isset($_SESSION['last_event_time']) ? $_SESSION['last_event_time'] : time();
$result = $db->quesry("SELECT * FROM event_noti WHERE event_time < {$time} ")->rows;
$_SESSION['last_event_time'] = time();
if($result){
$json = array();
foreach ($result as $row){
$json[] = array(
'text' => $row['event_text'],
'date' => date("Y-m-d H:i", $row['event_time']),
);
}
echo "data: ". json_encode($json) . "\n\n";
flush();
}

Fundamentally you cannot control this: it is a browser-specific setting.
If your browser is Firefox it appears to be controlled by this setting: dom.server-events.default-reconnection-time with a default of 5000ms.
Taking a step back: the reconnect only happens if the server closes the connection. Why are you closing the connection? (*) Why is a 3-second re-connection too fast?
The point of SSE is to minimize latency; the trade-off is more resource usage, in particular having to keep a dedicated socket open for each client.
So it sounds like you don't want to be using SSE, and instead want to use a simple AJAX poll, on a 60-second setInterval() call?
*: If you did intend to keep it open, you need to wrap your query and processing the result code in a while(true){...} loop. Put e.g. a one-second sleep at the end of the while loop to stop the DB server being overloaded.

Now I have my answer.
Controlling Reconnection-Timeout:
The browser attempts to reconnect the connection to the source within about 3 seconds after each server-sent event connection is closed. Before trying to reconnect, you can change that timeout by starting the line with retry: and then adding the millisecond number to wait.
I changed the code below and started working as I wanted.
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
include_once "config.php";
$time = isset($_SESSION['last_event_time']) ? $_SESSION['last_event_time'] : time();
$result = $db->quesry("SELECT * FROM event_noti WHERE event_time < {$time} ")->rows;
$_SESSION['last_event_time'] = time();
echo "retry: 60000\n"; // 60 seconds, to wait for next connection.
$json = array();
if($result){
foreach ($result as $row){
$json[] = array(
'text' => $row['event_text'],
'date' => date("Y-m-d H:i", $row['event_time']),
);
}
}
echo "data: ". json_encode($json) . "\n\n";
flush();
Source from

Related

How to fetch data every second from MYSQL database

I'm trying to make a Chat App using HTML, CSS, JS, PHP and Mysql.
I've completed all the functionalities that includes sending a message, receiving a message, displaying users... But the issue i'm facing is that i need to refresh the page every time i received a new message.
I'm looking for a way to auto update data with new data from mysql database.
Code:
<?php
if ($_GET['id']){
$id = $_GET['id'];
$id = preg_replace("/[^0-9]/", "", $id);
$fetching_messages = "SELECT * FROM users_messages WHERE from_user='$id' OR to_user='$id' ORDER BY id";
$check_fetching_messages = $db->prepare($fetching_messages);
$check_fetching_messages->execute();
$messages_all = $check_fetching_messages->fetchAll();
} else {
}
?>
<div id="autodata">
<?php foreach($to_users as $to_user) : ?>
<?php
$to_user_id = $to_user['to_user'];
$to_user_name = "SELECT * FROM users_accounts WHERE id='$to_user_id'";
$check_to_user_name = $db->query($to_user_name);
while ($row_to_user_name = $check_to_user_name->fetch()) {
$id_user = $row_to_user_name['id'];
$username = $row_to_user_name['username'];
$pdp = $row_to_user_name['profile_image'];
}
if ($id_user == $user_id){
} else {
echo '
<form style="height: fit-content;" name="goto'.$to_user_id.'" action="inbox.php">
<div onclick="window.location.replace('."'".'?id='.$to_user_id."'".')" class="inbox_chat_field_user">';
if (empty($pdp)){
echo "<img class='inbox_chat_field_user_img' src='uploads\profile\default.jpg'/>";
} else {
echo "<img class='inbox_chat_field_user_img' src='".$pdp."'/>";
}
echo '
<span class="inbox_chat_field_user_p">'.$username.'</span>
</div>
</form>
<hr class="inbox_separing_hr">';
}
?>
<?php endforeach;?>
</div>
Simply you can't do that, PHP is a server-side language, you can't tell the clients to refresh from PHP.
To accomplish that chat you should consider JavaScript in the browser.
The easiest way is by sending an AJAX request to your server and check if there are new messages every 5 or 10 seconds, and then do what you want with the messages in the response.
If you use jquery in your application you can send ajax request in this way:
$.get( "messages.php", function( data ) {
console.log( "Data Loaded: " + data );
});
and in messages.php script, you can fetch new messages from the database and return them with HTML or JSON format
You may also use FCM service offered by firebase to push your messages to the client directly, Check this package for PHP FCM.
There are other solutions like websockets etc...
It would have been easier for me to directly update your code had you separated business logic from presentation, so I am not going to attempt to do that. Instead I will describe a technique you can use and leave it to you to figure out the best way to use it. You might consider using server-sent events. See the JavaScript class EventSource.
The following "business logic" PHP program, sse_cgi.php, periodically has new output every 2 seconds (for a total of 5 times). In this case the output is just the current date and time as a string. But it could be, for example, a JSON record. Note the special header that it outputs:
<?php
header("Content-Type: text/event-stream");
$firstTime = True;
for ($i = 0; $i < 5; $i++) {
if (connection_aborted()) {
break;
}
$curDate = date(DATE_ISO8601);
echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser
while (ob_get_level() > 0) {
ob_end_flush();
}
flush();
if ($i < 4) {
sleep(2); # Sleep for 2 seconds
}
}
And this is the presentation HTML that would be outputted. JavaScript code in this case is just replacing the old date with the updated value. It could just as well append new <li> elements to an existing <ul> tag or <tr> elements to an existing <table>.
<html>
<head>
<meta charset="UTF-8">
<title>Server-sent events demo</title>
</head>
<body>
<div id='date'></div>
<script>
var evtSource = new EventSource('sse_cgi.php');
var date = document.getElementById('date');
evtSource.onmessage = function(e) {
// replace old content
date.innerHTML = e.data;
};
evtSource.onerror = function() {
// occurs when script terminates:
evtSource.close();
console.log('Done!');
};
</script>
</body>
</html>
Note that this presentation references the "business logic" scripts that returns the successive dates.
Important Note
It is important to realize that this technique keeps the connection to the server open for the duration until all the data has been ultimately sent and the business logic script ultimately terminates (or the presentation running in the browser issues a call to evtSource.close() to close the connection). So if you have a lot of simultaneous users, this could be an issue.
If your application does not have a limited number of messages to return then the previously described problem can be overcome by having the business logic script return immediately after having sent one message. This will break the connection with the browser, which if it is still there, will automatically attempt to re-connect with the business logic script (note that this reconnection can take a while):
Updated Business Logic
<?php
header("Content-Type: text/event-stream");
# Simulate waiting for next message:
sleep(2);
$curDate = date(DATE_ISO8601);
echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser
while (ob_get_level() > 0) {
ob_end_flush();
}
flush();
Updated Presentation
<html>
<head>
<meta charset="UTF-8">
<title>Server-sent events demo</title>
</head>
<body>
<div id='date'></div>
<script>
var evtSource = new EventSource('sse_cgi.php');
var date = document.getElementById('date');
evtSource.onmessage = function(e) {
// replace old content
date.innerHTML = e.data;
};
</script>
</body>
</html>

Data getting lost in server-sent event with PHP handler

I'm working on a one-way messaging system using server-sent events. I have a file (server.html) which sends the contents of a textarea to a PHP file (handler.php).
function sendSubtitle(val) {
var xhr = new XMLHttpRequest();
var url = "handler.php";
var postdata = "s=" + val;
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(postdata);
//alert(val);
}
This works (alert(val) displays the text in the textarea).
My handler.php code looks like this:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$stringData = $_POST['s'];
echo "data: Data is {$stringData}\n\n";
flush();
And the relevant part of my SSE receiver file (client.html) is as follows:
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("handler.php");
source.onmessage = function(event) {
var textarea = document.getElementById('subtitles');
textarea.value += event.data + "<br>";
textarea.scrollTop = textarea.scrollHeight;
};
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
The problem is that client.html only displays "data: Data is", so the text from server.html is getting lost somewhere along the way. I imagine it's the PHP code that's falling over, but I can't work out what's wrong. If anyone can help, I'd appreciate it.
EDIT
I chose to use SSE as opposed to websockets as I only need one-way communication: server.html should push the contents of its textarea to client.html whenever it changes. All the examples of SSE that I've looked at (and I've looked at a lot!) send "automatic" time-based data. I haven't seen any that use real-time user input. So perhaps I should clarify my original question and ask, "How can I use SSE to update a DIV (or whatever) in web page B whenever the user types in a textarea in web page A?"
UPDATE
I've narrowed the issue down to the while loop in the PHP file and have therefore asked a new question: Server-side PHP event page not loading when using while loop
Assuming you want to send a value from server.html and a value at client.html will be automatically updated...
You will need to store the new value somewhere because multiple instances of a script do not share variables just like that. This new value can be stored in a file, database or as a session variable, etc.
Steps:
Send new value to phpScript1 with clientScript1.
Store new value with phpScript1.
Connect clientScript2 to phpScript2.
Send stored value to clientScript2 if it is changed.
Getting the new value 'on the fly' means phpScript2 must loop execution and send a message to clientScript2 whenever the value has been changed by clientScript1.
Of course there are more and different approaches to achieve the same results.
Below there's some code from a scratchpad I've used in previous project.
Most parts come from a class (which is in development) so I had to adopt quite a lot of code. Also I've tried to fit it into your existing code.
Hopefully I didn't introduce any errors.
Do note I did not take any validation of your value into account! Also the code isn't debugged or optimized, so it's not ready for production.
Client side (send new value, e.g. your code):
function sendSubtitle(val) {
var xhr = new XMLHttpRequest();
var url = "handler.php";
var postdata = "s=" + val;
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(postdata);
//alert(val);
}
Server side (store new value):
<?php
session_start();
$_SESSION['s'] = $_POST['s'];
Client side (get new value):
//Check for SSE support at client side.
if (!!window.EventSource) {
var es = new EventSource("SSE_server.php");
} else {
console.log("SSE is not supported by your client");
//You could fallback on XHR requests.
}
//Define eventhandler for opening connection.
es.addEventListener('open', function(e) {
console.log("Connection opened!");
}, false);
//Define evenhandler for failing SSE request.
es.addEventListener('error', function(event) {
/*
* readyState defines the connection status:
* 0 = CONNECTING: Connecting
* 1 = OPEN: Open
* 2 = CLOSED: Closed
*/
if (es.readyState == EventSource.CLOSED) {
// Connection was closed.
} else {
es.close(); //Close to prevent a reconnection.
console.log("EventSource failed.");
}
});
//Define evenhandler for any response recieved.
es.addEventListener('message', function(event) {
console.log('Response recieved: ' + event.data);
}, false);
// Or define a listener for named event: event1
es.addEventListener('event1', function(event) {
var response = JSON.parse(event.data);
var textarea = document.getElementById("subtitles");
textarea.value += response + "<br>";
textarea.scrollTop = textarea.scrollHeight;
});
Server side (send new value):
<?php
$id = 0;
$event = 'event1';
$oldValue = null;
session_start();
//Validate the clients request headers.
if (headers_sent($file, $line)) {
header("HTTP/1.1 400 Bad Request");
exit('Headers already sent in %s at line %d, cannot send data to client correctly.');
}
if (isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] != 'text/event-stream') {
header("HTTP/1.1 400 Bad Request");
exit('The client does not accept the correct response format.');
}
//Disable time limit
#set_time_limit(0);
//Initialize the output buffer
if(function_exists('apache_setenv')){
#apache_setenv('no-gzip', 1);
}
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
while (ob_get_level() != 0) {
ob_end_flush();
}
ob_implicit_flush(1);
ob_start();
//Send the proper headers
header('Content-Type: text/event-stream; charset=UTF-8');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // Disables FastCGI Buffering on Nginx
//Record start time
$start = time();
//Keep the script running
while(true){
if((time() - $start) % 300 == 0){
//Send a random message every 300ms to keep the connection alive.
echo ': ' . sha1( mt_rand() ) . "\n\n";
}
//If a new value hasn't been sent yet, set it to default.
session_start();
if (!array_key_exists('s', $_SESSION)) {
$_SESSION['s'] = null;
}
//Check if value has been changed.
if ($oldValue !== $_SESSION['s']) {
//Value is changed
$oldValue = $_SESSION['s'];
echo 'id: ' . $id++ . PHP_EOL; //Id of message
echo 'event: ' . $event . PHP_EOL; //Event Name to trigger the client side eventhandler
echo 'retry: 5000' . PHP_EOL; //Define custom reconnection time. (Default to 3000ms when not specified)
echo 'data: ' . json_encode($_SESSION['s']) . PHP_EOL; //Data to send to client side eventhandler
//Note: When sending html, you might need to encode with flags: JSON_HEX_QUOT | JSON_HEX_TAG
echo PHP_EOL;
//Send Data in the output buffer buffer to client.
#ob_flush();
#flush();
}
//Close session to release the lock
session_write_close();
if ( connection_aborted() ) {
//Connection is aborted at client side.
break;
}
if((time() - $start) > 600) {
//break if the time exceeds the limit of 600ms.
//Client will retry to open the connection and start this script again.
//The limit should be larger than the time needed by the script for a single loop.
break;
}
//Sleep for reducing processor load.
usleep(500000);
}
You called handler.php first time in the server.html and again in client.html. Both are different processes. The variable state won't be retained in the web server. You need to store it somewhere if you want that value in another PHP process. May be you can use sessions or database.
While using sessions you can store the values in two files like:
<?php
//server.php
session_start();
$_SESSION['s'] = $_POST['s'];
And in client.php
<?php
//client.php
session_start();
echo "data: Data is ".$_SESSION['s']."\n\n";

SSE echo from PHP duplicating at random intervals

I'm trying to set up a PHP server send event, which works okay. But at random intervals it is pushing the same data repeatedly.
Here's a quick scenario to clarify what I'm describing: Let's say I insert a db record at 1:00:00. The record's data is pushed as it should. However, at 1:03:00 that record's data is pushed a second time. Then at 1:03:17, it is pushed again. And I now have 3 instances of the record displayed.
Why is this happening, and why at random intervals?
I increased php execution time, but the issue is still occurring.
In the browser console, I'm getting this error: net::ERR_INCOMPLETE_CHUNKED_ENCODING.
I have this for client side:
<script>
var source = new EventSource('pdo_updates.php');
var pdo_updates;
source.onmessage = function(e) {
pdo_updates = e.lastEventId + '' + e.data + '<br>';
document.getElementById("videoID").innerHTML += pdo_updates;
};
evtSource.close();
</script>
And this for server side:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0');
function send_msg($id, $msg) {
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$last_event_id = floatval(isset($_SERVER["HTTP_LAST_EVENT_ID"]) ? $_SERVER["HTTP_LAST_EVENT_ID"] : False);
if ($last_event_id == 0) {
$last_event_id = floatval(isset($_GET["lastEventId"]) ? $_GET["lastEventId"] : False);
}
$last_id = 0;
try {
$conn = new PDO('mysql:host=localhost;dbname=my_db', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
while(1) {
$id = $last_event_id != False ? $last_event_id : $last_id;
$stmt = $conn->prepare("SELECT id, message FROM messages WHERE id > :id ORDER BY id DESC LIMIT 1");
$result = $stmt->execute(array('id' => $id));
$stmt->bindValue('id', $id);
if ($result) {
while($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
if ($data) {
send_msg($data['id'], $data['message']);
$last_id = $data['id'];
}
}
}
sleep(1);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
So, after trying just about every keepalive related header, and every apache timeout adjustment config, I ended up running a packet capture. I discovered there was a TCP reset was being triggered by the remote end. I have my site behind Cloudflare, and once I disabled Cloudflare the issue partially disappeared. The TCP session was being refreshed every 100 seconds and would cause the last message to appear again when that happened. This was responsible for the browser console error: net::ERR_INCOMPLETE_CHUNKED_ENCODING
At the same time, however, there was an issue in my submit code. But I'm not 100% sure why.
$.ajax({
type: "POST",
url: url,
data: $("#submitmessage").serialize(), // serializes the form's elements.
success: function(data)
{
$("#myvideo").val("");
}
});
I needed to clear the value in my input field after submit. I haven't had any duplicates since.

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-stream\n\n");
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 "\n\n";
} 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 "\n\n";
$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.
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."\r\n";
echo "retry:".$retry."\r\n";
echo "data:" . json_encode( $data, JSON_FORCE_OBJECT|JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS );
echo "\r\n\r\n";
}
}
}
$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();
}
?>
While this is not a direct answer as to the problem, try using this method to find the error.. Your not getting errors, but this should help you find them maybe?
Basically you want to have a simple PHP script which includes your main script, but this page enables errors... Example below..
index.php / Simple Error Includer
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
require "other.php";
?>
other.php / You Main Script
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
weqwe qweqeq
qweqweqweqwe
?>
If you create a setup like this, if you view index.php you will see the following error Parse error: syntax error, unexpected 'qweqeq' (T_STRING) in /var/www/html/syntax_errors/other.php on line 5 because it does not have an invalid syntax on the main page and allows any includes to be error checked..
But if you where to view other.php, you would simply get a white / blank page because its unable to validate the whole page/script.
I use this method in my projects, that way regardless of what i do in other.php or any linked php pages, i will see an error report for them.
Please understand the code before commenting
to say this disables error control means you did not bother to RTM
Fill the buffer
Another issue in the past that i remember was filling the buffer before it would output to the browser. So try something like this before your loop.
echo str_repeat("\n",4096); // Exceed the required browser threshold
for($i=0;$i<70;$i++) {
echo "something as normal";
flush();
sleep(1);
}
Examples at http://www.sitepoint.com/php-streaming-output-buffering-explained/
It seems like the sleep function is interfering with the output. Putting the sleep function AFTERWARDS did work:
<?php
while(true) {
echo "data: This is the message.";
ob_flush();
flush();
sleep(3);
}
As other people suggest, I would encourage to use AJAX instead of an infinite loop, but that was not your question.
One thing I have noticed here is sleep() function in combination with ob_start() and - THERE IS NO - ob_start() anywhere in the full code example, yet there is flush() and ob_flush() ..
What are you flushing anyway?
And why not simply ob_end_flush() ?
The thing is that sleep() than echo(), than sleep() again, than echo() again, etc, etc.. has no effect when output buffering is turned on. Sleep function works as expected when output buffering is not in play - in between. In fact, it might *(and it will) produce quite unexpected results, and those results won't be the one we want to see.
The following code works fine here, also using Mayhem his str_repeat function to add 4k of data (that is usually the minimum for a tcp packet to be flushed by php)
echo str_repeat(' ', 4096);
while(true)
{
echo "data: This is the message.";
flush();
sleep(3);
}
Instead of using loop try this code given below which is working(tested myself) fine as per your requirement
echo "data: This is the message.";
$url1="<your-page-name>.php";
header("Refresh: 5; URL=$url1");
what this will do is it will call itself every 5 seconds (in your case set it to 3 instead of 5) and echo the output.
I am going to take a chance and state the obvious,
you could query the server every 3 seconds, and let the client do the waiting...
This could be done easily with javascript
for example, try this code and name if file.php
<?php
$action='';
if (array_key_exists('action',$_GET))
{$action=$_GET['action'];}
if ($action=='poll')
{
echo "this message will be sent every 3 sec";
}
else
{
?><HTML><HEAD>
<SCRIPT SRC="http://code.jquery.com/jquery-2.1.3.min.js"></SCRIPT>
<SCRIPT>
function doPoll()
{
$('#response').append($.get("file.php?action=poll"));
setTimeout(doPoll, 3000);
}
doPoll();
</SCRIPT>
</HEAD><BODY><DIV id="response"></DIV></BODY></HTML><?php
}
Could it be as simple as the script timing out?
Eventually PHP scripts self terminate if they run for too long. The solution for when you don't want this to happen is to keep resetting the time out.
So a simple addition might be all you need:
<?php
while(true) {
echo "data: This is the message.";
set_time_limit(30);
sleep(3);
ob_flush();
flush();
}
?>
Of course, that might not be it but my gut instinct is that this is the problem.
http://php.net/manual/en/function.set-time-limit.php
UPDATE: I noticed in the comments that you are using some free hosting. If they are running PHP in safe mode then you cannot reset your timeout.
I had the same issue and finally found the easy and quick solution on kevin choppin's blog:
Session Locks
First and foremost, if you're using sessions for whatever reason you will need to make them read-only on the stream. If they're writable, this will lock them everywhere else, so any page loads will hang while the server waits for them to become writable again. This is easily fixed by calling; session_write_close();
I suggest using if() statement instead of using while. And in your case your condition is always true, hence it is in infinite loop.

PHP Ajax: live report of the PHP algorithm

If I have the next algorithm in the file test.php:
for ($i = 0; $i < 1000; ++$i)
{
//Operations...
ALERT_TO_MAIN_PAGE($i);
}
I call test.php with AJAX form the page main.html
How can I track the progress of $i, with live values?
So main.html will show like this, very time the PHP file has completed one iteration:
Done 0.
Done 1.
Done 2.
...
You can try using HTML5 Server Sent Events to send messages like that to the browser.
Right off the website:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
/**
* Constructs the SSE data format and flushes that data to the client.
*
* #param string $id Timestamp/id of this connection.
* #param string $msg Line of text that should be transmitted.
*/
function sendMsg($id, $msg) {
echo "id: $id" . PHP_EOL;
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
You can't do it this way, because ajax waits for the operation to complete on the php side.
You should run your php offline (in background) and use ajax to simply ask for state (i.e. read info about progress from session).

Categories