PHP: How to progressively output inside a loop? - php

I have a loop and each iteration takes N seconds.
for($i=0;$i<10;$i++) {
echo $i;
process(); // 10 seconds process
}
Is there a way to output progressively (in web page) $i without having to wait until the whole loop ends ?

Flushes the system write buffers of PHP and whatever backend PHP is using (CGI, a web server, etc). This attempts to push current output all the way to the browser with a few caveats.
<?php
if (ob_get_level() == 0) ob_start();
for ($i = 0; $i<10; $i++) {
echo "<br> Line to show.";
echo str_pad('',4096)."\n";
ob_flush();
flush();
sleep(2);
}
echo "Done.";
ob_end_flush();
?>
Read documentation here.
EDIT:
str_pad() - Pad a string to a certain length with another string.
and Some versions of Microsoft Internet Explorer will only start to display the page after they have received 256 bytes of output, so you may need to send extra whitespace before flushing to get those browsers to display the page. that's why use str_pad() for be safe side.

Related

understanding flush,Ob_flush and buffer_output in PHP

i was reading about the buffering of contents and i found a simple script to show effects of flush
<?php
if (ob_get_level() == 0) ob_start();
for ($i = 0; $i < 10; $i++) {
echo "<br> Line to show.";
echo str_pad('', 4096) . "\n";
ob_flush();
flush();
sleep(2);
}
echo "Done.";
ob_end_flush();
?>
this script works fine and show the output , but when i remove the str_pad or reduce the length from 4096 to 40 flush does not work.
can any one help me out what exactly causing this..
Atlast i found the reason.
According to PHP.net Broswers require certain bytes to start the display of the page. Like some version of Internet Explorer require 200 bytes. And modern broswers like Firefox or Chrome requires more bytes to start Browser display.
In above case if you check it in Internet Explorer it will not require string padding but in Firefox or Chrome you need to Padd spaces with input to display flushed output.
I recognize this problem, had it once myself.. Do you have output compression activated?

PHP: Printing out status messages during runtime

I'm developing a long running php script that compiles scraped information from multiple sources, organizes them, and caches them in a database.
As this script has a very high runtime, I'd like to print out runtime status reports in order to track the progress.
for ($i = 1; $i<= 10; $i++) {
echo "Starting iteration #$i\n";
set_time_limit(40);
echo "Time Limit set, starting 10 second sleep.\n";
sleep(10);
echo "Finishing iteration #$i\n\n";
}
echo "Iterations Finished.";
would output:
Starting iteration #1
Time Limit set, starting 10 second sleep
then wait 10 seconds and output:
Finishing iteration #1
Starting iteration #2
Time Limit set, starting 10 second sleep
then right before the php finishes parsing, it will output:
Iterations Finished.
What is the best way to achieve this?
If you are running PHP from the CLI you can output directly to stdout, without having to wait for the script
to end, using :
$handle = fopen('php://stdout', 'r');
fwrite($handle, $output);
If run from CGI, which would be really bad for a script like this, you would have to change how the buffer acts with flush();
Try writing the runtime status reports to a file, then viewing live updates of the file using ajax, per this question: Live feed of an updating file

echo/print issue in php while loop

I need a fix for this.here is just part of my code
<?php
$number = 30;
while($number > 0) {
$number--;
sleep(30);
print "$number . Posted<br>";
}
?>
The loop process in the loop is actually much bigger, i just put the important stuff.
Anyways as you can see it should print
30 posted
(wait 30 seconds)
29 Posted
(wait 30 seconds)
28 Posted
(wait 30 seconds)
But instead it waits till the loop is over, then just prints it all at once. Is there a fix for this? I was thinking an ajax method, but I dont know of any.
Nice that everyone explained why.
This is because by default PHP will process everything before it 'flushed' anything out to the browser. By just printing each line, it's storing that information in the buffer which will all be printed simultaneously once PHP is finished executing.
If you want PHP to flush that content to the browser immediately after the line, you need to call flush() after each one, then it will output the text one line at a time after each one is called.
Call flush() after printing.
You need to use flush()
An example of a loop with flush() is
<?php
ob_start();
for ($i = 0; $i < 10; $i++)
{
echo "<div>" . time() . ": Iteration $i</div>";
sleep(1);
ob_flush();
flush();
}
ob_end_flush();
?>
You should not flush often because you force php to process messages and this will increase the time of execution.
You might put \n in echo or print to flush the buffer.

Seeking STDOUT in PHP

I have a php script that is running in CLI and I want to display the current percent progress so I was wondering if it is possible to update the STDOUT to display the new percent.
When I use rewind() or fseek() it just throws an error message.
See this code:
<?php
echo "1";
echo chr(8);
echo "2";
The output is only 2 since "chr(8)" is the char for "backspace".
So just print the amount of chars you need to go back and print the new percentage.
Printing "\r" works too on Linux and Windows but isn't going to cut it on a mac
Working example:
echo "Done: ";
$string = "";
for($i = 0; $i < 100; ++$i) {
echo str_repeat(chr(8), strlen($string));
$string = $i."%";
echo $string;
sleep(1);
}
Output \r and then flush to get back to the first column of the current line.
Writing to a console/terminal is surprisingly complex if you want to move backwards in the output raster or do things like add colours - and the behaviour will vary depending on the type of console/terminal you are using. A long time ago some people came up with the idea of building an abstract representation of a terminal and writing to that.
See this article for details of how to do that in PHP.

Blocking file-read in php?

I want to read everything from a textfile and echo it. But there might be more lines written to the text-file while I'm reading so I don't want the script to exit when it has reached the end of the file, instead I wan't it to wait forever for more lines. Is this possible in php?
this is just a guess, but try to pass through (passthru) a "tail -f" output.
but you will need to find a way to flush() your buffer.
IMHO a much nicer solution would be to build a ajax site.
read the contents of the file in to an array. store the number of lines in the session. print the content of the file.
start an ajax request every x seconds to a script which checks the file, if the line count is greater then the session count append the result to the page.
you could use popen() inststed:
$f = popen("tail -f /where/ever/your/file/is 2>&1", 'r');
while(!feof($f)) {
$buffer = fgets($f);
echo "$buffer\n";
flush();
sleep(1);
}
pclose($f)
the sleep is important, without it you will have 100% CPU time.
In fact, when you "echo" it, it goes to the buffer. So what you want is "appending" the new content if it's added while the browser is still receiving output. And this is not possible (but there are some approaches to this).
I solved it.
The trick was to use fopen and when eof is reached move the cursor to the previous position and continue reading from there.
<?php
$handle = fopen('text.txt', 'r');
$lastpos = 0;
while(true){
if (!feof($handle)){
echo fread($handle,8192);
flush();
$lastpos = ftell($handle);
}else{
fseek($handle,$lastpos);
}
}
?>
Still consumes pretty much cpu though, don't know how to solve that.
You may also use filemtime: you get latest modification timestamp, send the output and at the end compare again the stored filemtime with the current one.
Anyway, if you want the script go at the same time that the browser (or client), you should send the output using chunks (fread, flush), then check any changes at the end. If there are any changes, re-open the file and read from the latest position (you can get the position outside of the loop of while(!feof())).

Categories