error: incomplete chunked encoding in php scripts - php

Trying to run some php scripts on a new shared server account, the scripts constantly hang and timeout with no error messages. Only when running the scripts in Chrome with the web developer console up did I get a hint of what was going on: "net::ERR_INCOMPLETE_CHUNKED_ENCODING" is what it would say.
Fiddler2 was slightly more specific:
Fiddler.Network.ProtocolViolation - [#165] Transfer-Encoding: Chunked response did not terminate with a proper zero-size chunk.
...and:
Fiddler.Network.ProtocolViolation - [#165] [HTTPLint #M012] The HTTP Chunked response body was incomplete; most likely lacking the final 0-size chunk.
I made a test script to demonstrate the problem. All it did was sleep() for a random 20-60 seconds, then display a random string. Ten iterations by default.
But the helpdesk at this hosting provider kept insisting that it had something to do with a hard 60 second timeout in the php environment, which I knew was hogwash. So I put together a second test script that used Ajax to call another script multiple times in succession. Again, it was bone simple, just a sleep() for a random time between 40 and 55 seconds, then generate and display a short random string of characters.
So it could never take more than 60 seconds to execute. But it still kept failing, albeit with "net::ERR_EMPTY_RESPONSE" for the xhr call in the Chrome console instead of "net::ERR_INCOMPLETE_CHUNKED_ENCODING".
When I showed this to them, they actually admitted there was a problem, which felt like a victory. But it was short-lived. The very next day they came back and said, so sorry, they couldn't figure out what was causing this, nothing they can do, have I considered upgrading to a vps?
I have become somewhat obsessed. I want to know what the hell is causing this.
The code for the first test script I wrote:
<?php
set_time_limit(3600);
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[mt_rand(0, $charactersLength - 1)];
}
return $randomString;
}
isset($_REQUEST["limit"]) ? $limit = $_REQUEST["limit"] : $limit = 10;
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">';
echo "<title>Test Script</title></head><body>";
//ob_start ();
echo "<br><br>A random delay between 20 and 60 seconds will be generated, then a randomly generated string will be displayed<br>The default limit on iterations is ten<br>When the script terminates normally, the phrase \"test complete\" will be output at the bottom<br><br>";
flush();
for ($i = 0; $i < $limit; $i++) {
$delay = mt_rand (20, 60);
echo "<br><br><br>iteration ".($i+1)." - script will now sleep for $delay seconds";
flush();
sleep ($delay);
echo "<br><br>Here is a random string:<br>";
$length = mt_rand (50, 100);
echo generateRandomString($length);
flush();
}
echo '<br><br>...test complete</body></html>';
ob_end_flush();
?>
You can go to this page: http://www.scripttest1.cu.cc/test_script.php to see for yourself what happens when it is run on this server.
The code for the second test script:
<?php
set_time_limit(600);
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
isset($_REQUEST["limit"]) ? $limit = $_REQUEST["limit"] : $limit = 15;
isset($_REQUEST["longorshort"]) ? $longorshort = $_REQUEST["longorshort"] : $longorshort = "long";
$start = 1;
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">';
echo "<title>Test Script 2</title></head><body>";
echo '<script src="test_script_js7.js"></script>';
echo "
<br>Clicking the button below will start the script running
<br>The script called via XMLHttpRequest will generate a random delay between 40 and 55 seconds (5-10 seconds if \"longorshort\" is set to \"short\" in the url string), then a randomly generated string will be displayed in the table on the bottom
<br>The default limit on iterations is fifteen
<br>When the process terminates normally, the phrase \"test complete\" will be output in the \"Main Info\" cell
<br><b>The script called via XMLHttpRequest will <u>never take more than 60 seconds</u> to complete processing</b>
";
//echo "<img src=http://nzbstar.info/download_batch.png onclick=\"getter($limit, '$start', '$longorshort')\"><br>";
echo "<br><img src=start.jpg onclick=\"getter_outer($limit, '$start', '$longorshort')\"><br>click here to abort ";
echo "<table border=1><tr>";
echo "<td valign=top><div id=main_info>Main Info:</div></td>";
echo "<td valign=top><div id=iteration>Iterations:</div></td>";
echo "<td valign=top><div id=message>Messages:</div></td>";
echo "</tr></table>";
echo "<table border=1 style=table-layout:fixed;><tr>";
for ($i = 1; $i <= $limit; $i++) {
echo "<td valign=top><div class=getter id=post_$i><i>Result $i</i></div></td>";
if ( ($i%5 == 0) ) {echo "</tr><tr>";}
}
echo "</tr></table>";
echo '</body></html>';
?>
...the javascript:
var master_off = false;
var getter_running_now = false;
function getter_outer(limit, i, longorshort){
if (getter_running_now) {alert ("Script is already running!"); return;}
getter_running_now = true;
if (master_off) {document.getElementById("main_info").innerHTML += "<br>master switch off, aborting!"; return;}
if (i >= limit) {document.getElementById("main_info").innerHTML += "<br>Got to the next getter when we shouldn't have, there's a bug!"; return;}
getter(limit, i, longorshort);
}
function getter(limit, i, longorshort){
if (master_off) {document.getElementById("main_info").innerHTML += "<br>master switch off, aborting!"; return;}
if (i >= limit) {document.getElementById("main_info").innerHTML += "<br>Got to the next getter when we shouldn't have, there's a bug!"; return;}
var post_number_element = "post_" + i;
var batch_result_script = "test_script_2a.php?longorshort=" + longorshort;
document.getElementById("iteration").innerHTML = "Iterations:<br>Doing iteration: " + i;
document.getElementById(post_number_element).innerHTML = "doing this one...<br>";
var xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4){
if (xmlhttp.status==200){
var result = "<b>result for " + post_number_element + " is:</b><br>" + xmlhttp.responseText + "<br><span style=color:green;>Success!</span>";
document.getElementById(post_number_element).innerHTML = result;
i++;
if (i >= limit) {
document.getElementById("main_info").innerHTML += "<br><span style=color:green;>Test complete!</span>";
}
else {getter(limit, i, longorshort);}
}
else {document.getElementById("message").innerHTML += "<br> - <span style=color:red;>http return status for iteration " + i + " was " + xmlhttp.status + "</span>";}
}
}
xmlhttp.open("GET",batch_result_script,true);
xmlhttp.send();
}
function master_switch(){
master_off = true;
document.getElementById("main_info").innerHTML += "<br><span style=color:red;>Master Switch Off!</span>";
}
...and the script it calls via ajax:
<?php
set_time_limit(600);
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[mt_rand(0, $charactersLength - 1)];
}
return $randomString;
}
if ( isset($_REQUEST["longorshort"]) && $_REQUEST["longorshort"] == "short" ) {$delay = mt_rand (5, 10);}
else {$delay = mt_rand (40, 55);}
sleep ($delay);
echo "<br>Here is a random string:<br>";
$length = mt_rand (5, 10);
echo generateRandomString($length);
?>
Go here: http://www.scripttest1.cu.cc/test_script_2.php?longorshort=long to see it in action. Unlike with the first script, the error it generates is "empty response". But I assume the two are related.
Both these scripts work fine on both another shared server, and a vps with LAMP, to which I have access. They also work on this server, for a while, before they fail. The first test script usually gets through a few iterations before it dies. The second one, with the Ajax, will sometimes run right through to completion if the "longorshort" is set to "short".
Also, the first test script works flawlessly from the command line. The second one of course won't work right in that environment.
The server runs PHP Version 5.4.44 and Apache Version 2.4.16 under Linux.
Google has not been my friend. I posted to Stackoverflow and got exactly 5 (five) views, zero responses.
Can anyone here at least toss me a clue? Or failing that, point me to an alternative to Stackoverflow where they might actually answer my question?

Found an answer with your wireshark info, as with all delay wrote like that, the 58sec delay bugged me.
When Internet Explorer establishes a persistent HTTP connection with a Web server (by using Connection: Keep-Alive headers), Internet Explorer reuses the same TCP/IP socket that was used to receive the initial request until the socket is idle for one minute. After the connection is idle for one minute, Internet Explorer resets the connection. A new TCP/IP socket is used to receive additional requests. You may want to change the HTTP KeepAliveTimeout value in Internet Explorer.
https://support.microsoft.com/en-us/kb/813827
That let me think other browser must follow that standard, so you must send something on the line when you script run to prevent closing the connection, as you can't ask your customers to change their timeout value.

Related

PHP Batch upload array data

I am working with a client API (master API) that does not have a bulk feature.
I have taken data from 2 different API's (client API's) and merged it into one JSON file that is properly formatted. Checked in online JSON Validator.
The JSON File is 1100 records of merged customer data. Taking one record at a time, I have built a function that submits the data successfully to the master API.
I have now built a PHP script that loops through the JSON File and takes the row data (each client record) and submits it to the master API successfully. After about 90 rows, the PHP script times out.
I have set the following code on the page
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
set_time_limit(600);
#ob_end_clean();
And am buffering each update to return a JSON status code returned from the master API.
What should I be doing to get the PHP to not time out after about 100 records and keep updating the buffer response on the page.?
Thanks in advance.
Jason
I've taken a few of different approached to this kind of problem in the past. The only quick fix for this is if you can change the php.ini settings on the server to increase the timeout enough to allow your batch to complete. This is not a great solution, but it is a solution.
The next option (in ascending order of effort) is to set up a loop between the browser and your server where your browser makes a request, the server sends a portion of the records, then returns to the browser with a cursor indicating where the process left off, the browser makes another request to the server, sending the cursor back as a parameter, and this continues until the batch finishes. This is nice because you can display a progress bar to the user.
Finally, you could have an agent running on the server that waits for batch jobs to be submitted and runs them completely outside of the HTTP request lifecycle. So your browser makes a request to kick off a batch job, which results in some sort of record in a database that can keep track of the status of the job. The agent picks up the job and sets it to a pending state while it works, then sets the completion status when it finishes. You can set set something up that allows you to poll the server from the browser periodically so you can alert the user when the process finishes. Of you can just have the agent send an email report to the user when the batch completes. This is the most solid option with the least risk of something interrupting the processing, and it can leave an audit trail without any effort at all. But it's clearly more complicated to set up.
Thanks Rob.
Your response sent me in the right direction.
I sort of used your backend idea on the frontend. I just looped through 20 records at a time and then refreshed the page via javascript and started at 21 to 40 etc. Threw in a progress bar for fun as well.
Thanks for helping me get my head around the idea. Not the right way to do it, but my Python is just as bad as my PHP.
<?php
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
set_time_limit(600);
#ob_end_clean();
require("header.php");
require_once('nav.php');
function sync_systems($sfData){
$dataPost = $sfData;
$postID = $dataPost['ID'];
$epcall = update_profile($dataPost);
$epResult = json_decode($epcall, true);
if($epResult['status'] != 404){
$sfStatus = updateSFOpportunity($epResult['client_id'], $epResult['ep_id'] );
if($sfStatus == 1){
$datamsg = " Success! The sync was a success in both Salesforce and other system. OtherSystem Record " . $epResult['ep_id'] . " was created or updated.<br/>";
} else {
$datamsg = " Success! The sync was a success in other system, but failed in Salesforce<br/>";
}
echo json_encode(['code'=>200, 'msg'=>$datamsg]);
} else {
$datamsg = " Failure! The sync did not work.<br/>";
echo json_encode(['code'=>404, 'msg'=>$datamsg]);
} // end epResult
}
function sync_ep($sfData){
$dataPost = $sfData;
$postID = $dataPost['ID'];
$epcall = update_profile($dataPost);
$epResult = json_decode($epcall, true);
if($epResult['status'] != 404){
// $sfStatus = updateSFOpportunity($epResult['client_id'], $epResult['ep_id'] );
if($sfStatus == 1){
$datamsg = " Success! The sync was a success in both Salesforce and other system. Other System Record " . $epResult['ep_id'] . " was created or updated.<br/>";
} else {
$datamsg = " Success! The sync was a success in other system, but failed in Salesforce<br/>";
}
echo json_encode(['code'=>200, 'msg'=>$datamsg]);
} else {
$datamsg = " Failure! The sync did not work.<br/>";
echo json_encode(['code'=>404, 'msg'=>$datamsg]);
} // end epResult
}
$ju = "CustomerData20Fall.json";
//read json file from url in php
$readJSONFile = file_get_contents($ju);
//convert json to array in php
$jfile = json_decode($readJSONFile);
//print_r($jfile);
//convert json to array in php
$epSync = array();
$oldValue = 0;
$total = count($jfile );
?>
<!-- Progress bar holder -->
<div id="progress" style="width:500px;border:1px solid #ccc;"></div>
<!-- Progress information -->
<div id="information" style="width"></div>
<?php
if(isset($_REQUEST["NEXTVALUE"])){
$nextValue = $_REQUEST["NEXTVALUE"];
} else {
$nextValue = 1;
}
$refreshValue = $nextValue + 20;
$displaycounter = $nextValue;
$timeRemaining = 0;
$updatedRecords = 0;
foreach ($jfile as $key => $jsons) {
$newKey = $key;
if($oldValue != $newKey){
if($newKey >= $nextValue && $newKey < $refreshValue){
// echo "Updated: " . [$oldValue]['EPID'] . "<br/>";
// echo "<hr>" . $nextValue . " >= " . $newKey . " < " . $refreshValue;
print_r($epSync[$oldValue]);
$displaycounter = $newKey;
echo sync_systems($epSync[$oldValue]);
usleep(30000);
flush();
} else {
if($key == ($refreshValue)){
$theURL = "sf-ep-sync.php?NEXTVALUE=" . $refreshValue . "&RAND=" . rand();
// echo "<hr>" . $newKey . " = " . $refreshValue . " " . $theURL ."<br/>";
echo "<script>location.href = '" . $theURL . "';</script>";
exit;
}
}
$oldValue = $newKey;
$i = $nextValue + 1;
if(($i + 1) == $total ){
$percent = intval($i/$total * 100)."%";
$timeRemaining = 0;
} else {
$percent = intval($i/$total * 100)."%";
$timeRemaining = (($total - $displaycounter)/60);
}
usleep(30000);
echo '<script language="javascript">
document.getElementById("progress").innerHTML="<div style=\"width:'.$percent.';background-color:#ddd;\"> </div>";
document.getElementById("information").innerHTML="'.$displaycounter.' row(s) of '. $total . ' processed. About ' . round($timeRemaining, 2) . ' minutes remaining.";
</script>';
// This is for the buffer achieve the minimum size in order to flush data
echo str_repeat(' ',1024*64);
}
foreach($jsons as $key => $value) {
$epSync[$newKey][$key] = $value;
}
}
Thanks,
Jason

PHP Ad Rotation Script Randomly Stops Working after updating a part of the code

I had a PHP script that is supposed to automatically rotate advertisement banners, and it was working just fine before, but after i tried changing 1 piece of the code it stopped working. Even after i changed it back it still wouldn't work anymore. I don't get any errors, and no error_log files, the only part of the code i changed was just the url of the page its supposed to redirect you to. Here's my code (before the change):
<?php
$bCount = 1;
$bCode[$bCount] = '<img src="/img/ad1.gif"/>';
$bCount++;
$bTotals = $bCount -1;
if ($bTotals>1)
{
mt_srand((double)microtime() * 1234567);
$bPick = mt_rand(1, $bTotals);
}
else
{
$bPick = 1;
}
$ad = $bCode[$bPick];
?>
and here is the code after i made the small change (really not much of a difference, just a change in the URL):
<?php
$bCount = 1;
$bCode[$bCount] = '<img src="/img/ad1.gif"/>';
$bCount++;
$bTotals = $bCount -1;
if ($bTotals>1)
{
mt_srand((double)microtime() * 1234567);
$bPick = mt_rand(1, $bTotals);
}
else
{
$bPick = 1;
}
$ad = $bCode[$bPick];
?>
After i made that change, the script wouldn't display the ad that was supposed to show up, it was just blank. So i rolled back the changes to how it was before when it was working, and it still will not show up anymore. What could have caused it to stop working?

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.

Splitting PHP script to avoid timeout

I have the following script which is used to PDF some pages on a website:
<?
include_once('phpToPDF.php') ;
phptopdf_url( 'http://www.example.com/csb04-termination-box/?style=alt' ,'pdf/', 'csb04-termination-box.pdf');
phptopdf_url( 'http://www.example.com/csb05-termination-box/?style=alt' ,'pdf/', 'csb05-termination-box.pdf');
phptopdf_url( 'http://www.example.com/csb06-compact-termination-box/?style=alt' ,'pdf/', 'csb06-compact-termination-box.pdf');
echo 'Done';
?>
The problem I have is if I add many more pages the server times out before the script has finished running. I've tried changing set_time_limit(0); but that hasn't helped (on shared hosting).
What would be the best way to amend the script so it can finish? I considered trying to split it in to multiple scripts and running it via ajax but not sure where to start with that as have no prior experience with it.
The easiest way to do it would be this :
include_once('phpToPDF.php') ;
$i = isset($_GET['i']) ? intval($_GET['i']) : 0;
if ($i == 0) phptopdf_url( 'http://www.example.com/csb04-termination-box/?style=alt' ,'pdf/', 'csb04-termination-box.pdf');
if ($i == 1) phptopdf_url( 'http://www.example.com/csb05-termination-box/?style=alt' ,'pdf/', 'csb05-termination-box.pdf');
if ($i == 2) phptopdf_url( 'http://www.example.com/csb06-compact-termination-box/?style=alt' ,'pdf/', 'csb06-compact-termination-box.pdf');
if ($i < 3) header('Location: ?i=' . ($i + 1));
echo 'Done';
Basically this lets your webbrowser do the counting, without using Ajax. When one request is done, the browser automatically loads the next page, until the counter ($i) is at 3.
Use the following script to process all your files, one at a time:
include_once('phpToPDF.php') ;
$urls = array();
$urls[] = array('http://www.example.com/csb04-termination-box/?style=alt', 'csb04-termination-box.pdf');
$urls[] = array('http://www.example.com/csb05-termination-box/?style=alt', 'csb05-termination-box.pdf');
$urls[] = array('http://www.example.com/csb06-termination-box/?style=alt', 'csb06-termination-box.pdf');
// add here more elements in $urls if needed
if(!isset($_GET['n']))
$n = 0;
else
$n = intval($_GET['n']);
if(isset($urls[$n]))
{
phptopdf_url($urls[$n][0] ,'pdf/', $urls[$n][1]);
header('Location: ?n='.($n+1)); // calls the conversion of the next file in the $urls array
}
else
echo 'Done';

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