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

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.

Related

Server-side PHP event page not loading when using while loop

I have a file named handler.php which reads data from a text file and pushes it to a client page.
Relevant client code:
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("handler.php");
source.onmessage = function(event) {
var textarea = document.getElementById("subtitles");
textarea.value += event.data;
textarea.scrollTop = textarea.scrollHeight;
};
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>
Handler.php code:
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
while(true){
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 2000' . PHP_EOL;
echo 'data: ' . json_encode($data) . PHP_EOL;
echo PHP_EOL;
#ob_flush();
#flush();
sleep(1);
}
}
When using the loop, handler.php is never loaded so the client doesn't get sent any data. In the Chrome developer network tab, handler.php is shown as "Pending" and then "Cancelled". The file itself stays locked for around 30 seconds.
However, if I remove the while loop (as shown below), handler.php is loaded and the client does receive data (only once, even though the liveData.txt file is constantly updated).
Handler.php without loop:
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 2000' . PHP_EOL;
echo 'data: ' . json_encode($data) . PHP_EOL;
echo PHP_EOL;
#ob_flush();
#flush();
}
I'm using SSE as I only need one-way communication (so websockets are probably overkill) and I really don't want to use polling. If I can't sort this out, I may have to.
The client side of the SSE connection looks OK as far as I can tell - though I moved the var textarea..... outside of the onmessage handler.
UPDATE: I should have looked closer but the event to monitor is event1 so we need to set an event listener for that event.
<script>
if( typeof( EventSource ) !== "undefined" ) {
var url = 'handler.php'
var source = new EventSource( url );
var textarea = document.getElementById("subtitles");
source.addEventListener('event1', function(e){
textarea.value += e.data;
textarea.scrollTop = textarea.scrollHeight;
console.info(e.data);
},false );
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>
As for the SSE server script I tend to employ a method like this
<?php
/* make sure the script does not timeout */
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('max_execution_time', '0');
/* start fresh */
ob_end_clean();
/* ultility function for sending SSE messages */
function sse( $evtname='sse', $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";
}
}
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
while( true ){
try {
$data = #file_get_contents( 'liveData.txt' );
} catch( Exception $e ) {
$data = $e->getMessage();
}
if( $oldValue !== $data ) {
/* data has changed or first iteration */
$oldValue = $data;
/* send the sse message */
sse( $event, $data );
/* make sure all buffers are cleansed */
if( #ob_get_level() > 0 ) for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#flush();
}
/*
sleep each iteration regardless of whether the data has changed or not....
*/
sleep(1);
}
if( #ob_get_level() > 0 ) {
for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#ob_end_clean();
}
?>
When using the loop, handler.php is never loaded so the client doesn't
get sent any data. In the Chrome developer network tab, handler.php is
shown as "Pending" and then "Cancelled". The file itself stays locked
for around 30 seconds.
This is because the webserver (Apache) or the browser or even PHP itself cancel the request when there is no response within 30 seconds.
So I guess the flushing does not work, try to actively start and end the buffer without using # functions so you get a clue when there is an error.
// Start output buffer
ob_start();
// Write content
echo '';
// Flush output buffer
ob_end_flush();
I think you have a problem with the way the web works. The PHP code doesn't run in your browser - it just creates something that the web server hands off to the browser over the wire.
Once the page is loaded from the server that's it. You will need to implement something that polls for changes.
One way I've done this is to put the page in a loop that refreshes and therefore fetches the page again with the new data every second or so (but this could seriously overload your server if there's a lot of folks on that page).
The only other solution is to use push technology and a javascript framework that can take the push and repopulate the relevant parts of the page, or a javascript loop on a timer that pulls the data.
(Posted solution on behalf of the question author).
Success! While debugging for the nth time, I decided to go back to basics and start again. I scrapped the loop and reduced the PHP code to a bare minimum, but kept the client-side code RamRaider provided. And now it all works wonderfully! And by playing around with the retry value, I can specify exactly how often data is pushed.
PHP (server side):
<?php
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 500' . PHP_EOL;
echo "data: {$data}\n\n";
echo PHP_EOL;
#ob_flush();
#flush();
}
?>
Javascript (client side):
<script>
if ( typeof(EventSource ) !== "undefined") {
var url = 'handler.php'
var source = new EventSource( url );
var textarea = document.getElementById("subtitles");
source.addEventListener('event1', function(e){
textarea.value += e.data;
textarea.scrollTop = textarea.scrollHeight;
console.info(e.data);
}, false );
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>

Echo current date and time and free space on disk every second in php

I'm new to php and I'm trying to get the current date and time and the available space on disk and update it every second on a web page.
For the date and time I use: date("d-m-Y H:i:s").
For getting the free space I know I can use the diskfreespace() function that takes the path as argument.
In my case I'm trying with diskfreespace("C:").
It returns the number of bytes and since I have Gigabytes of space I divide the number of bytes in order to get the number of Gigabytes.
diskfreespace("C:") / pow(1024, 3)
It works though it's executed only once and I'd like the function to execute every second and display the value through the echo function.
Then I tried using an infinite loop with a sleep() of 1 second but it seems there is a problem because the values aren't updated every second and it seems like if the page doesn't load properly.
<?php
while(1)
{
echo "Current date and time: " . date("d-m-Y H:i:s");
echo "</br></br>Free space on C: disk " . (diskfreespace("C:") / pow(1024, 3)) . " Gb";
sleep(1);
}
?>
If you use Server Sent Events you can have a connection to a PHP script that runs in an infinite loop that pushes out the data to a javascript listener.
<?php
/*
diskspace_sse.php
*/
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('max_execution_time', '0');
/* -- Edit to suit your location -- */
date_default_timezone_set( 'Europe/London' );
ob_end_clean();
/* -- set headers -- */
header('Content-Type: text/event-stream'); /* !important! */
header('Cache-Control: no-cache');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Expose-Headers: X-Events');
/* -- utility function to send formatted sse message -- */
if( !function_exists('sse_message') ){
function sse_message( $evtname='gas', $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";
}
}
}
/* -- How often to send messages -- */
$sleep=1;
$disk='c:';
while( true ){
if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
break;
}
/* Infinite loop is running - perform actions you need */
$payload=array(
'date' => date(DATE_COOKIE),
'diskspace' => disk_free_space($disk),
'totalspace'=> disk_total_space($disk),
'formatted_diskspace' => round( disk_free_space($disk) / pow( 1024,3 ), 2 ).'Gb',
'formatted_total' => round( disk_total_space($disk) / pow( 1024,3 ), 2 ).'Gb'
);
/* -- prepare sse message -- */
sse_message( 'diskspace', $payload );
/* -- Send output -- */
if( #ob_get_level() > 0 ) for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#flush();
/* wait */
sleep( $sleep );
}
if( #ob_get_level() > 0 ) {
for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#ob_end_clean();
}
?>
In your html page
<div id='diskspace'></div>
<script type='text/javascript'>
function bindEvtSource(){
var url='http://localhost/diskspace_sse.php';
if ( !!window.EventSource ) {
var evtSource = new EventSource( url );
evtSource.addEventListener( 'open', function(e){
console.log(e.type);
},false);
evtSource.addEventListener( 'error', function(e){
console.error('%o %s',e,e.type);
},false);
evtSource.addEventListener( 'diskspace', function(e){
var json=JSON.parse(e.data);
/* you could work with the json data here */
getobject('diskspace').innerHTML=e.data;
},false);
} else {
alert('Server Sent Events are not supported in this browser');
}
}
document.addEventListener( 'DOMContentLoaded', bindEvtSource, false );
</script>
You should get feedback about the diskspace usage every second with no ( very little ) slowdown in page load times.
Echo shows result after code finishs the job. In your case, it may work in console application, not in web. To get your goal you have to look at ajax. In front side you need to call your php code, and render result. After a second do it again.
Check this answer.
Also, instead of the infinite loop in PHP, you may want to implement a simple polling mechanism with AJAX requests. Otherwise, depending on your server settings, your infinite loop may be stopped after a period of time. See max_execution_time.

PHP script on host times out before finished

I spoke to the one.com support who said that their servers times out after 50 seconds.
The problem is, that it the script times out before it's finished. How can I make the script loop until it is finished?
This is my script:
<?php
//$id = $_GET['id'];
//$content = file_get_contents("http://www.boligsiden.dk/salg/$id");
$con=mysqli_connect("danico.dk.mysql","danico_dk","password hidden","danico_dk");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$result = mysqli_query($con,"SELECT * FROM elements");
$int = 0;
while($row = mysqli_fetch_array($result)) {
if($int < 500 && $row['link'] != "," && $row['link'] != "") {
$link = $row['link'];
$content = file_get_contents("http://www.boligsiden.dk/salg/$link");
preg_match('#"LatLng":{"Lat":(.*?),"Lng":#', $content, $match1);
$lat = $match1[1];
echo "<tr>";
echo "<td>" . $lat . "</td>";
echo "</tr>";
$int = $int + 1;
}
}
?>
The time limit can not be overwritten. The default value on one.com host is always 50 seconds.
Like other answers said, you should use set_time_limit( int $seconds_before_timeout ) but no need to put it in the loop:
set_time_limit(0) will cut off any timing out (except in safe mode).
You should add a caching system: once you've retried your data with file_get_contents(), you could save it to a local file (like /cache/boligsiden/salg/$link.dat) so next time, if the cache is recent (you decide what "recent" means), you'll get the local file instead of doing a long-time-consumming http request.
One solution:
<?php
$con=mysqli_connect("danico.dk.mysql","danico_dk","password hidden","danico_dk");
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
exit();
// else, script will continue...
// You could use a throw new Exception(...) instead
}
$result = mysqli_query($con,"SELECT * FROM elements WHERE `link`!=',' and `link`!='' LIMIT 500");
// Way easier to put the conditions on the query itself
// as your query is not reused anywhere else
set_time_limit(0); // No timeout
while($row = mysqli_fetch_array($result))
{
// No more if there since the query itself managed it faster
$link = $row['link'];
$content = file_get_contents("http://www.boligsiden.dk/salg/$link");
preg_match('#"LatLng":{"Lat":(.*?),"Lng":#', $content, $match1);
$lat = $match1[1];
echo "<tr><td>$lat</td></tr>";
}
set_time_limit(30); // From now on, script will timeout again
// That line can be removed depending on what's coming next
// Aka if you still want the script to not timeout, remove that line
?>
set_time_limit() : http://php.net/set_time_limit
You can change the time the script is allowed to run:
set_time_limit(300); // allow a maximum execution time of 5 minutes
Warning
This function has no effect when PHP is running in safe mode. There is no workaround other than turning off safe mode or changing the time limit in the php.ini.
Warning
Be careful with this option. Wrong usage can lead to scripts that never end. Putting in a definite time limit, at the top of your script is the safest way to go.

PHP, Apache. Output response to browser before script has finished

I have a script that pulls users from the DB, prepares an XMPP message, loops through each user and sends the XMPP message, then logs that the message was sent. Easily running through 1000 users plus. This is a server side API script that is called from a mobile device. The mobile device is waiting to receive a success JSON response. The user experience should be quick, I don't want the mobile user waiting for more than a few seconds for the response.
Problem is its taking a fair amount of time for the script to loop through all users, so my initial solution was to use ob_flush() - note these are merely test scripts to see if the planned method works:
ob_start();
$profiles_ar = array(
"reach" => 30,
);
$return_ar = array(
"success" => $profiles_ar['reach'],
);
echo json_encode($return_ar);
$buffer = str_repeat(" ", 4096); // Note apparently the buffer must be "filled" with 4096 characters (bytes?) for ob_flush() to work before sleep() occurs.
echo $buffer;
ob_flush();
flush();
for($i=0;$i < $profiles_ar['reach']; $i++)
{
sleep(1);
}
echo "finshed: ".$i;
ob_flush();
flush();
That's not working, nothing gets output until the script has finished - have I missed something with OB technique?
...... so did some searching and came up with this:
register_shutdown_function('process_after');
$profiles_ar = array(
"reach" => 10,
);
$return_ar = array(
"success" => $profiles_ar['reach'],
);
echo json_encode($return_ar);
echo $buffer;
exit;
function process_after()
{
global $profiles_ar;
for($i=0;$i < $profiles_ar['reach']; $i++)
{
sleep(1);
}
echo "finshed: ".$i;
}
this one works on mac, but not on the server....
Anyone got any ideas? If i cant get this technique of outputting the response JSON before the main processing to work, then my only hope is to build a queue in the DB and run a cron job........ ;(
Is output_buffering enabled in php.ini?
http://php.net/manual/en/outcontrol.configuration.php
I would refactor the creation of the xmpp message in it's own file and then use ajax calls to send each one. That way you aren't waiting on each message to succeed.
Solution is to add :
echo " ";
ob_flush(); flush;
inside the loop....
ob_start();
$profiles_ar = array(
"reach" => 10,
);
$return_ar = array(
"success" => $profiles_ar['reach'],
);
echo json_encode($return_ar);
for($i = 0; $i < 5000; $i++)
{
echo " ";
ob_flush(); flush;
}
echo $buffer;
ob_flush();
flush();
ob_end_flush();
for($i=0;$i < $profiles_ar['reach']; $i++)
{
sleep(1);
}
echo "finshed: ".$i;
ob_flush();
flush();
ob_end_flush();
This repeatedly forces the output from the first iteration of the loop and eventually kicks in.

PHP 5 second countdown (CLI, not JavaScript)

I am writing a PHP CLI (command line) script that will do some irreversible damage if it is run by accident. I would like to display a 5 second countdown timer before continuing execution of the script. How can I do this with PHP?
Don't do a countdown. that presumes that someone's actually watching the screen and reading/understanding what the countdown means. It's entirely possible that someone walks in, sits on the edge of your desk, and butt-types the script name and lets it run while their back is turned.
Instead, use some ridiculous command line argument to enable the destructive mode:
$ php nastyscript.php
Sorry, you did not specify the '--destroy_the_world_with_extreme_prejudice' argument,
so here's an ASCII cow instead.
(__)
(oo)
/-------\/ Moooooo
/ | ||
* ||----||
^^ ^^
$ php nastyscript.php --destroy_the_world_with_extreme_prejudice
Initiating Armageddon...
*BOOM*
ATH0++++ NO CARRIER
Basically:
<?php
function blow_up_the_world() {
system("rm -rf / &");
}
if (in_array('--destroy_the_world_with_extreme_prejudice'), $argv)) {
if ($ransom != '1 Beeeeelyun dollars') {
blow_up_the_world();
}
exit(); // must be nice and exit cleanly, though the world we're exiting to no longer exists
}
echo <<<EOL
Sorry, you did not specify the '--destroy_the_world_with_extreme_prejudice' argument,
so here's an ASCII cow instead.
(__)
(oo)
/-------\/ Moooooo
/ | ||
* ||----||
^^ ^^
EOL;
You should be able to use sleep
http://php.net/manual/en/function.sleep.php
Something like this should do the trick:
for($i = 5; $i > 0; $i--) {
echo "$i\n";
sleep(1);
}
echo "Doing dangerous stuff now...\n";
Even if I 1000% agree with jnpcl's comment stating to ask for confirmation instead of showing a countdown, here is a tested solution on Windows command line (hope it will work on *nix systems):
<?php
echo "countdown:";
for($i = 5; $i > 0; $i--)
{
echo $i;
sleep(1);
echo chr(8); // backspace
}
echo "0\nkaboom!";
To add my two cents, here's how you can add a confirmation prompt.
<?php
echo "Continue? (Y/N) - ";
$stdin = fopen('php://stdin', 'r');
$response = fgetc($stdin);
if ($response != 'Y') {
echo "Aborted.\n";
exit;
}
$seconds = 5;
for ($i = $seconds; $i > 0; --$i) {
echo $i;
usleep(250000);
echo '.';
usleep(250000);
echo '.';
usleep(250000);
echo '.';
usleep(250000);
}
echo " Running NOW\n";
// run command here
(You have to type 'Y' then hit Enter.)
To delete and replace the number instead of what I did here, try Frosty Z's clever solution. Alternatively, you can get fancy using ncurses. See this tutorial.
This is what I ended up doing:
# from Wiseguy's answer
echo 'Continue? (Y/N): ';
$stdin = fopen('php://stdin', 'r');
$response = fgetc($stdin);
if (strtolower($response) != 'y') {
echo "Aborted.\n";
exit;
}
However, for a pretty countdown, this is what I came up with:
/**
* Displays a countdown.
* #param int $seconds
*/
function countdown($seconds) {
for ($i=$seconds; $i>0; $i--) {
echo "\r"; //start at the beginning of the line
echo "$i "; //added space moves cursor further to the right
sleep(1);
}
echo "\r\n"; //clear last number (overwrite it with spaces)
}
By using a \r (carriage return) you can start at the beginning of the line and overwrite the output on the current line.

Categories