Control php output and post it as it occurs - php

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.

Related

Output part of the HTML file before running time-consuming commands

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

is it acceptable to use ob_start()/ob_clean/ob_flush() to do error handling in PHP?

Is it acceptable to do error handling in php like below? I am not very familiar with ob_start()/ob_clean()/ob_flush(), so I want to known is there any bad effect to use them? for example, will they affect performance?
<?php
function custorErr()
{
ob_clean();
//echo error message and some other error handling...
die();
}
set_error_handler("custorErr");
ob_start();
?>
<!doctype html>
<!-- html here -->
<html>
<head>
</head>
<body>
demo
</body>
</html>
<?php ob_flush();?>
If this is not the best practice, then is there any better way to clear all the page content when there being error?
I guess your approach would work very fine and if you want to use it repeatedly I think your approach is the best.
Another option is to put the buffering part in a try/catch-block. And then clean up output buffer if something goes wrong. If it's just one location you want to check this error , I believe this approach would be better.
<?php
try {
ob_start();
?>
<!doctype html>
<!-- html here -->
<html>
<head>
</head>
<body>
demo
</body>
</html>
<?php
ob_flush();
}
catch {Exception $e) {
ob_end_clean(); //OR ob_clean();
echo 'error is ' . print_r($e, true);
}
Some documentation about cleaning up output buffering:
http://php.net/manual/en/function.ob-end-clean.php
http://php.net/manual/en/function.ob-clean.php

Is output buffering possible inside of flush?

I'm trying to show function status as its looping but delete the previous status before the next one shows. Current code:
<?php
#ini_set('zlib.output_compression',0);
#ini_set('implicit_flush',1);
#ob_end_clean();
set_time_limit(0);
// Perform 1st function here
echo "Retrieving Data...";
echo str_repeat(' ',1024*64);
sleep(1);
// Perform 2nd function here
echo "Analyzing Data...";
echo str_repeat(' ',1024*64);
sleep(1);
// Perform 3rd function here
echo "Done...";
echo str_repeat(' ',1024*64);
sleep(1);
// Clean all echos here..
?>
<html>
<head>
// Dynamic head content as a result of the php functions above
</head>
<body>
</body>
</head>
Now this works, but displays all the echos one after the other. I'd like the next status to replace the first, until the end, then remove "Done" before the html is displayed.
I tried:
ob_start();
echo "Retrieving Data...";
echo str_repeat(' ',1024*64);
sleep(1);
ob_end_clean();
But that didn't work. Is this possible at all?
The following uses the last-of-type pseudo CSS selector to hide all the old progress status messages. It's a reasonably new selector so it doesn't work well on older browsers (pre IE9), you can check the compatibility on the Mozilla Developer Network
<html>
<head>
<style type="text/css">
#progress span {
display: none;
}
#progress span:last-of-type {
display: block; !important
}
</style>
</head>
<body>
<div id="progress">
<?php
for ($i =0; $i<=100; $i+=10) {
ob_start();
echo "<span>$i%</span>";
ob_end_clean();
sleep(1);
}
?>
</div>
</body>
</html>
Output your lines with a \r. This will return the cursor to 0 on the same line. Where you can write over it.
echo "Retrieving Data..\r";
sleep(3);
echo "Analyzing Data...\r";
sleep(3);
echo "Done... \r\n";
I added a \n to the last echo otherwise the command prompt would over write the last echo.

PHP Error: ob_flush() [ref.outcontrol]: failed to flush buffer. No buffer to flush

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

Methods ob_start and ob_flush don't work, why?

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).

Categories