I am using ob_start()/ob_flush() to, hopefully, give me some progress during a long import operation.
Here is a simple outline of what I'm doing:
<?php
ob_start ();
echo "Connecting to download Inventory file.<br>";
$conn = ftp_connect($ftp_site) or die("Could not connect");
echo "Logging into site download Inventory file.<br>";
ftp_login($conn,$ftp_username,$ftp_password) or die("Bad login credentials for ". $ftp_site);
echo "Changing directory on download Inventory file.<br>";
ftp_chdir($conn,"INV") or die("could not change directory to INV");
// connection, local, remote, type, resume
$localname = "INV"."_".date("m")."_".date('d').".csv";
echo "Downloading Inventory file to:".$localname."<br>";
ob_flush();
flush();
sleep(5);
if (ftp_get($conn,$localname,"INV.csv",FTP_ASCII))
{
echo "New Inventory File Downloaded<br>";
$datapath = $localname;
ftp_close($conn);
} else {
ftp_close($conn);
die("There was a problem downloading the Inventory file.");
}
ob_flush();
flush();
sleep(5);
$csvfile = fopen($datapath, "r"); // open csv file
$x = 1;
// skip the header line
$line = fgetcsv($csvfile);
$y = (feof($csvfile) ? 2 : 5);
while ((!$debug) ? (!feof($csvfile)) : $x <= $y) {
$x++;
$line = fgetcsv($csvfile);
// do a lot of import stuff here with $line
ob_flush();
flush();
sleep(1);
}
fclose($csvfile); // important: close the file
ob_end_clean();
However, nothing is being output to the screen at all.
I know the data file is getting downloaded because I watch the directory where it is being placed.
I also know that the import is happening, meaning that it is in the while loop, because I can monitor the DB and records are being inserted.
Any ideas as to why I am not getting output to the screen?
You also need to check the PHP settings
some installs default to 4096, some default to off
output_buffering = Off
output_buffering = 4096
agreed with George but do check the above settings
Make sure that your output buffering doesn't start automatically. Run:
print ob_get_level ();
before ob_start (); if will will see something else then 0 you've got the answer.
Hey man I was also got stuck in this problem
and finally got the correct solution
here it is for you
you have to add content type for your page
you can do that by two ways
1. using html tag
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
Ex.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Wp Migration</title>
</head>
<body>
<?php
for($i=0;$i<70;$i++)
{
echo 'printing...<br>';
ob_flush();
flush();
sleep(3);
}
?>
</body>
</html>
using php header function
<?php header( 'Content-type: text/html; charset=utf-8' ); ?>
Ex.
<?php
header( 'Content-type: text/html; charset=utf-8' );
for($i=0;$i<70;$i++)
{
echo 'printing...<br>';
ob_flush();
flush();
sleep(3);
}
?>
All the best
Ob_end_clean() discards the contents of the current output buffer and turns off the buffering.
You should use ob_end_flush() instead.
Add this line
header("X-Accel-Buffering: no");
worked for me.
You can edit it with the .htaccess file
To disable output buffering, modify the line as follows:
php_value output_buffering Off
php_value output_buffering 4096
worked for me. Thank you!
Check this site: Click Here
It's possible that your webserver is doing its own buffering. Probably with something like mod_gzip.
Here is some very simple test code:
<?php
echo 'starting...<br/>';
for($i = 0; $i < 5; $i++) {
print "$i<br/>";
flush();
sleep(2);
}
print 'DONE!<br/>';
If it takes 10 seconds for that page to load, rather than seeing a new line every 2 seconds, then it means that it is being cached by your webserver. For what you are trying to do, there is no need to use ob_start and ob_flush. Just call flush whenever you want to force the content to the browser. However, like I mentioned, if the webserver is waiting for the content to complete before sending, then that won't do anything for you.
Edit: Another possibility is that you're viewing the page from behind a corporate or ISP proxy/firewall that waits for the whole page before serving it (so that it can scan it to see if it looks like pornography, for example).
Related
I'm trying to use output buffers in PHP to update the interface of the web page.
The simplified HTML structure is as follow:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>Some Header Here</h1>
<?php
ob_implicit_flush(true);
$buffer = str_repeat(" ", 4096)."\r\n<span></span>\r\n"; // to ensure the browser to print ("flush") the output
ob_start();
echo '<p>Start operation</p>' . $buffer . PHP_EOL;
ob_flush(); flush(); ob_clean();
$response = shell_exec('a_long_running_command1'); // which will execute for around 3 minutes
echo '<p>Command 1 Response: ' . $response . '</p>' . PHP_EOL;
ob_flush(); flush(); ob_clean();
$response = shell_exec('a_long_running_command2'); // which will execute for around 5 minutes
echo '<p>Command 2 Response: ' . $response . '</p>' . PHP_EOL;
ob_flush(); flush(); ob_clean();
ob_end_flush();
echo '<p>Task Complete</p>' . PHP_EOL;
?>
</body>
</html>
whereas Command 1 & 2 are CPU-intensive tasks running on a Linux-based server.
However, once entered the page, the page is blank (and the browser (Chrome) indicates it keeps loading) until Command 1 is complete. How can I display the first <h1> header and the "Start Operation" heading first?
One more greedy question: is it possible for me to display the elapsed time on the page so that the user knows the command is still running?
I know I can achieve this via AJAX calls, but I don't want the user to:
refresh the page (which might lead to Command 1 & 2 being run for more than 1 instance in the server)
know the AJAX call so that he can replay the AJAX call
I Was wondering if it was possible to refresh a php webpage when a files content has changed?
I have some code but it doesn't refresh on a file change it refreshes every second. How can I do it when the file changes?
Here is the code:
<html>
<head>
<title>PHP Page</title>
<meta http-equiv="refresh" content="1">
</head>
<body>
<?php
$filename = "data.txt";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
echo "<pre>$contents</pre>";
fclose($handle);
?>
</body>
you can try this
header( "Refresh:5; url=http://www.example.com/page2.php", true, 303);
You can fire an ajax request on every 5 seconds which will check either file is changed or not.
If file is changed the reload the page with javascript using
location.reload();
I am just a amateur php programmer!
I have a specific requirement.
I want to control php output behaviour in my script.
What i want is this thing
myscript.php
echo "phase 1";// (output to browser immediately)
echo "Proceeding further....";
sleep (10);
echo "phase 2";// (output to the browser immediately)
sleep(10);
echo "phase end";
But what happens is all the output of echo gets dump after 20 second i mean complete output not sequencewise...
How can i output it in sequence wise.. i do not know what it is called ! Please also show a example if possible it helps in understanding
You might wanna take a look at some Websocket like Rachet. For a simpler version, you just use a combination of Javascript/Ajax and PHP.
EDIT: The simplest way; the jQuery/PHP way
As requested in the comments, this is a very simple, insecure way of achieving asynchronous server work/feedback.
Server-side
//doTheWork.php
switch($_POST['step'])
{
case 1:
$output = shell_exec('cat text.txt'); //Do whatever you need to do here
break;
case 2:
$output = shell_exec('ls');
break;
default:
$output = "No or invalid step declared";
}
echo $output;
Client-side
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Testing live feedback</title>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script> <!-- Include your local version of jQuery -->
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
</head>
<body>
<div id="console">
<h2>Output from server</h2>
<div id="console_output">
</div>
</div>
<script type="text/javascript">
var totalSteps = 5;
var currentStep = 0;
var url_to_server = "http://localhost/doTheWork.php";
function executeAndOutputData()
{
currentStep++;
if(currentStep >= totalSteps) return;
$.post(url_to_server, {step:currentStep}, function(response){
$("#console_output").append(response+"<br>");
executeAndOutputData();
});
}
executeAndOutputData();
</script>
</body>
</html>
Yes you can natively with PHP using flush functions.
Example with your code :
<?
echo "phase 1";// (output to browser immediately)
echo "Proceeding further....";
flush();
ob_implicit_flush(true);
ob_flush();
sleep (10);
echo "phase 2";// (output to the browser immediately)
sleep(10);
echo "phase end";
?>
edit : But, this is just to answer you. A such code is certainly fully useless and must be improved to remove sleep calls.
I am satisfied with Dencker answer but still i now know a simpler way by using only PHP.
Derived from http://www.codeproject.com/Tips/680085/Real-time-Updating-of-PHP-Output
// Turn off output buffering
ini_set('output_buffering', 'off');
// Turn off PHP output compression
ini_set('zlib.output_compression', false);
//Flush (send) the output buffer and turn off output buffering
while (#ob_end_flush());
// Implicitly flush the buffer(s)
ini_set('implicit_flush', true);
ob_implicit_flush(true);
echo "Start<br />";
echo str_pad("",1024," ");
echo "<br />";
ob_flush();
flush();
sleep(5);
echo "Program Output";
ob_flush();
flush();
And it does work without any server config and it is well suited for my need (temp). But i will be looking at the suggestion posted here.
Could someone please save these 2 files and run them and tell me why I get the error " ob_flush() [ref.outcontrol]: failed to flush buffer. No buffer to flush". I tried googling around and it says that I have to use ob_start(); but when I do then it doesn't print out line by line, but rather returns the whole object from the FOR loop when it has completed. I'm kinda new to PHP so I'm not sure where else to look..
test_process.php
// This script will write numbers from 1 to 100 into file
// And sends continuously info to user
$fp = fopen( '/tmp/output.txt', 'w') or die('Failed to open');
set_time_limit( 120);
ignore_user_abort(true);
for( $i = 0; $i < 100; $i++){
echo "<script type=\"text/javascript\">parent.document.getElementById( 'foo').innerHTML += 'Line $i<br />';</script>";
echo str_repeat( ' ', 2048);
flush();
ob_flush();
sleep(1);
fwrite( $fp, "$i\n");
}
fclose( $fp);
main.html
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css" media="screen">
.msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
.new{ background-color:#3B9957;}
.error{ background-color:#992E36;}
</style>
</head>
<body>
<iframe id="loadarea" width="1024px" height="768px"></iframe><br />
<script>
function helper() {
document.getElementById('loadarea').src = 'test_process.php';
}
function kill() {
document.getElementById('loadarea').src = '';
}
</script>
<input type="button" onclick="helper()" value="Start">
<input type="button" onclick="kill()" value="Stop">
<div id="foo"></div>
</body>
</html>
You only need ob_flush() if an output buffer is active (for example by ob_start(), or by configuration settings). If you haven't, just remove the ob_flush(). Or you can make it conditional:
if (ob_get_level() > 0) {ob_flush();}
I think you are confusing ob_flush() with flush(). While ob_start() and ob_flush() handles a PHP internal output buffer that catches all outputs, flush() is the normal function that flushes STDOUT like in other programming languages.
Example:
<?php
ob_start();
echo "Foobar\nFoobar\nFoobar\n";
// Nothing printed yet
ob_flush(); // Now it is printed.
echo "Foobar\n"; // Printed directly, because contains a line ending.
echo "Foobar"; // Not printed, because normally buffers are flushed on line endings
flush(); // Printed.
EDIT:
Your output is not printed, because your webserver may buffer the contents. Try to turn off compression and output buffering:
#apache_setenv('no-gzip', 1);
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
Please also keep in mind, that Safari and Internet Explorer have an internal 1K buffer. So you need to add 1 KB of padding data (like spaces), to make them render.
EDIT 2:
Your implementation is broken. You want to poll your data with ajax. Use jQuery on the client side:
<div id="counter">0%</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
<script type="text/javascript">
function doPoll(){
$.post('script-that-returns-stuff.php', function(data) {
$("#counter").html(data);
setTimeout(doPoll,5000);
});
}
doPoll();
</script>
Then in script-that-returns-stuff.php:
<?php
$file = explode("\n", file_get_contents("/tmp/output.txt"));
$last_line = $file[count($file)-1];
echo $last_line."%";
Where is ob_start()?
ob_flush flushes the output buffer to your file handle. Maybe you have it wrong.
An example:
ob_start(); //start output buffering
echo 'hello world'; //not outputed
ob_flush(); //sends the output buffer so displays hello world.
manual
i want to add Transfer-Encoding: chunked header to the file that i'm outputing (its just generated plain text), but when i add:
header("Transfer-Encoding: chunked");
flush();
the browser doesn't want to open the file.
The webpage at ... might be
temporarily down or it may have moved
permanently to a new web address.
what i need to do for it to work?
You need to send the Content-Length with every chunk you send. Look at Wikipedia for a first impression, how a chunked encoding looks like. Its not that trivial and in many cases its oversized.
Update:
First you send the headers, because they must always send before any content (also with chunked encoding). Then you send (for every chunk) the size (in hexadecimal) followed by the content. Remember flush() after every chunk. At last you must send a zero-size chunk to make sure, that the connection get closed properly.
Its not tested, but something like this
header("Transfer-Encoding: chunked");
echo "5\r\n";
echo "Hello";
echo "\r\n\r\n";
flush();
echo "5\r\n";
echo "World";
echo "\r\n";
flush();
echo "0\r\n\r\n";
flush();
As previous members said you have to follow chunked transfer encoding format. In next example i will show how you can use one user function to follow format rules:
<?php
//set headers
header('Transfer-Encoding: chunked');
header('Content-Type: text/html');
//browsers collect first 1024 bytes
//and show page only if bytes collected
//so we will use space padding.
//if you cannot understand what it means
//check script with PADDING=0
define("PADDING", 16);
//caret return and new line characters as constant
define("RN", "\r\n");
//user function what get current output buffer data
//and prefixes it with current buffer length.
//next it call flush functions
function flush_data(){
$str=ob_get_contents();
ob_clean();
echo dechex(strlen($str)).RN.$str.RN;
ob_flush();
flush();
}
//default HTML 5 page
echo "<!doctype html><html><head><title>Transfer-Encoding: chunked</title>";
echo "<script>";
//+padding
for($i=0;$i<PADDING;$i++){
//64 spaces (1 block)
echo " ";
}
echo "</script></head><body><div>";
//current output buffer will shown immediately in browser
//after this function
flush_data();
//cycle wait 1 sec before next iteration
for($i=0;$i<5;$i++)
{
//print iteration number
echo "$i<br>";
flush_data();
sleep(1);
}
echo "</div></body></html>".RN;
//terminating part of encoding format
flush_data();
echo "0\r\n\r\n";
ob_flush();
?>
Notes:
Check if «implicit_flush» is On in your php.ini
Know if you overflow output buffer («output_buffering» in php.ini) it will flush automatically.
For me when I was trying something with "Transfer-Encoding: chunked" I had to use this code to make it work:
<?php
echo "data";
header_remove("Transfer-Encoding");
flush();
?>
This code will still have the "Transfer-Encoding: chunked" header.
It automatically sets the Transfer-Encoding heading when you use flush but when it set it manually it fails, so to prevent any problems try to remove it. Also make sure that you remove the heading on the line before you do your first flush to prevent errors.
Use ob_flush(); before flush();
Sample code:
<?php
header('Content-Encoding', 'chunked');
header('Transfer-Encoding', 'chunked');
header('Content-Type', 'text/html');
header('Connection', 'keep-alive');
ob_flush();
flush();
$p = ""; //padding
for ($i=0; $i < 1024; $i++) {
$p .= " ";
};
echo $p;
ob_flush();
flush();
for ($i = 0; $i < 10000; $i++) {
echo "string";
ob_flush();
flush();
sleep(2);
}
?>