Echo messages while php script still executes - php

I have a php script that uses cURL and takes about 10-15 minutes to execute. What it does, it parses about 1000 pages looking for specific matches and throughout the script I have diagnostic messages echo'ed out, like "Going to the next page", "Found a match", "Error loading page" ... The way it works now (and the way that it's normal) is it executes for like 10 minutes and only then spits out all my custom messages.
I would like to be able to display those messages as they happen, not when the script is done executing. I was thinking something like AJAX would do it, but am not sure how it would work. Any tips are greatly appreciated. Thanks.

So, this is a old post but I found a solution for this. As I also have to make the same thing, output when the script is still running. Not any answer from here helped.
First of all, I am using Win32 server(production) and XAMPP as local for tests. This example is just a proof of concept and can be modified as you please.
<?php
ob_implicit_flush(true);
for($i=1; $i<=10; $i++){
echo "$i ...<br>";
for($k = 0; $k < 40000; $k++) echo ' ';
sleep(1);
}
?>
So, we open output buffer as implicit. Then we make a demo loop to count from 1 to 10 and display the values as they are been processed. Second loop will fill in the browsers buffer. And finally to check if everything is working well we make a sleep for 1 second. Otherwise the script will run too fast and we could not know if we achieved the goal.
Hope this helps !

You could create a staging table.
The PHP script could, instead of echo'ing the message, store them into a database table (possibly memory table for performance).
You could then periodically poll a seperate PHP script using ajax, which would query the table, and return any new messages to the client.

Use flush to immediately send output to the browser, by flushing the output buffer.
echo "foo";
flush();
echo "bar";
flush();

Actually you're looking for something like flush and ob_flush, however bear in mind that there are a lot of factors that can prevent your output from being flush'd as it happens.
From the flush documentation you'll get:
Several servers, especially on Win32, will still buffer the output from your script until it terminates before transmitting the results to the browser.
Server modules for Apache like mod_gzip may do buffering of their own that will cause flush() to not result in data being sent immediately to the client.

I'm using the #ob_flush() after every echo. In this example PHP_EOL creates a new line after $string
function output($string){
echo $string.PHP_EOL;
#ob_flush();
}

Basically, have your script write HTML output to a temporary log file. Then use ajax to periodically update the end-user's browser with the temporary log file. jQuery will make quick work of this.
Ajax is the only guaranteed way to get it to work on all browsers. Here is a quote from PHP's flush page.
flush() may not be able to override
the buffering scheme of your web
server and it has no effect on any
client-side buffering in the browser.
It also doesn't affect PHP's userspace
output buffering mechanism. This means
you will have to call both ob_flush()
and flush() to flush the ob output
buffers if you are using those.

Sounds to be like you have output buffering turned on.
Calling ob_end_flush() will print what's currently in the buffer, and turn off the buffer for the rest of the script execution.

You can use the flush() function to send all the content of the buffer to the client. http://php.net/manual/fr/function.flush.php

You could use both flush and ob_flush, reminding to set the content type header:
<?php
header( 'Content-type: text/html; charset=utf-8' );
for( $i = 0 ; $i < 10 ; $i++ ){
echo $i . '<br>';
flush();
ob_flush();
sleep(1);
}
Source: dermeister note in php.net ob_flush page.
Tested on Firefox 42.0 and Chrome 46.0

Related

Multiple flush statements in PHP and Apache

I' am using XMLHttpRequest 2 to get chunked response through it's onprogress event and displaying it in browser. Everything seems to work fine but I have a little confusion. Consider the following code:
<?php
echo "I' am sending ";
ob_flush();
flush();
echo "content to client.";
ob_flush();
flush();
This is pretty standard PHP way of sending chunked response to client. However I was expecting to get output as two different chunks, one containing first echo's result and other containing second's. But I' am getting a single combined chunk of both echo statements. When I put sleep(1) after first flush() then everything seems to work fine.
My question is that is there some deliberate wait on webserver side to wait for some other content before sending already received chunk or is there some thread scheduling mechanism that is forcing network layer of Apache to wait for it's turn.
Edit
I forgot to mention that I have disabled webserver's output buffering which means if I flush something from PHP it will get sent to browser.
Any help is highly appreciated.
Thanks.
See: http://www.icemelon.com/tutorials/20/Output_While_Script_Still_Running.htm
You may be seeing browser output buffering here. Try adding:
for($k = 0; $k < 40000; $k++)
echo ' ';
Right before you flush in order to fill up the browser's buffer and make it spit out your output.

PHP endless while loop confusion

I'm trying to figure out why this loop doesn't return anything to the browser:
while(1) {
echo "hello";
flush();
sleep(1);
}
I'm expecting it to return "hello" to the browser every second... am I wrong? Right now the page just seems to hang.
PHP only outputs after execution has finished. so all you are doing where is generating a new hello every milisecond, and since you never exit the loop, you never see the output.
To correct my answer and make you to understand better, and for the AJAX lovers...
you need and extra flush there.. the 'ob_' one:
<?php
while( 1 ):
echo "hello";
ob_flush( ); flush();
sleep( 1 );
endwhile;
This is the 'trick' for everyone who need to know ;)
The browser won't display anything until the entire page is received. PHP is not capable of what you're trying to accomplish.
In Javascript, this is pretty simple.
<script>
window.setInterval(function(){document.innerHTML += "<br> Hello"}, 1000)
</script>
You should realise that PHP is a scripting language in the sense that it returns the output only after completing the script. EDIT: Or after output buffers are filled, thanks #Marc B.
Regardless I would say it is wiser to use JS for this or if you really need your server, use AJAX requests.
Perhaps you should consider using Javascript? That will allow you to add content every second (do keep in mind that JS is run at the clientside though, so you might not want to make your operations all that expansive then.)
Alternatively you could consider using AJAX requests through for instance JQuery, but that might be outside the scope of this question...
Maybe is not to late to answer but if you want to flush every second here I give you a sample:
<?php
echo "Flushing every second ...\n";
flush( );
$seconds = array (0,1,2,3,4,5,6,7,8,9);
foreach ($seconds as $second):
echo $second . "\n";
sleep( 1 );
#ob_flush( ); flush( );
endforeach;
echo 'I flashed 10 second :P';
Valentin gave you the right answer (upvote him/accept his answer!), but didn't explain why. Here's the explanation:
Output buffering
PHP doesn't output to the browser immediately, it waits to have some amount of content to send to the browser (probably it sends in chunks of 1024, 2048 or 4096 bytes), or when the execution ends. Calling flush() makes PHP send the data, but there is more than one layer of buffering. One is the internal buffering (what I've just commented), but you can add more layers of buffering. Suppose this code:
<?php
echo "hi";
setcookie('mycookie', 'somevalue');
?>
The setcookie() function sends an http header to the browser, but it can't do it because in HTTP, the server (or the client, it is the same both ways) must send first all headers, a blank line, and then the contents. As you see, you are outputting some content (hi) before the header, so it fails (because the internal buffering follows the same order of execution).
You can add another layer of output buffering, using the php functions ob_*(). With ob buffering it only buffers content output, not HTTP headers. And you can use them to get the output of functions that directly output to the browser, like var_dump(). Also, you can nest layers of ob:
<?php
// start first level of output buffering
ob_start();
echo "nesting at level ", ob_get_level(), "<br />\n"; // prints level 1
echo "hi<br />";
ob_start();
echo "nesting at level ", ob_get_level(), "<br />\n"; // prints level 2
var_dump($_POST);
$post_dump = ob_get_clean();
// this will print level 1, because ob_get_clean has finished one level.
echo "nesting at level ", ob_get_level(), "<br />\n";
echo "The output of var_dump(\$_POST) is $post_dump<br />\n";
// in spite of being inside a layer of output_buffering, this will work
setcookie('mycookie', 'somevalue');
// flush the current buffer and delete it (will be done automatically at the
// end of the script if not called explicitly)
ob_end_flush();
Probably your PHP server has output_buffering enabled by default. See the configuration variables to turn it off/on by default.
Ok, Carlos criticize me because I didn't explained my answer but also his answer is to vague... with cookies, layers.. POST, ob_levels... :OO to much info with no real point about the real question of the user but I will tell you why your code is not working. Because you have set in the php.ini the output buffering something like:
output_buffering = On
or
output_buffering = 4096 (default setting on most distributions)
Thats why you need the extra 'ob_flush( )', to get rid of any garbage output..
so... To make your code work you have 2 options:
1). set output_buffering = 0 or Off (if you have access to the php.ini)
2). ob_flush many times as layers of buffering you have
If you don't know how many layers you have you can do something like:
while (#ob_end_clean( ));
and clean every garbage you can have, and then your code will work just fine..
Complete snipp:
<?php
while (#ob_end_clean( ));
while(1) {
echo "hello";
flush();
sleep(1);
}
Cya..
Adding to all the other answers,
To do asynchronous Server push to clients you'll need to use WebSockets. It's a vast subject and not fully standardized, but there are certainly ways of doing it. If you are interested search for PHP Websockets.

Showing PHP output continuously?

I have a script that runs for like 1 hour.
I have 2 webhosts, in one of them, I can see the output in realtime, (it loops a million times and echoes stuff) while on the other webhost, I have to wait until the script is finished running before I see any output at all.
How can I change the settings to show the output in realtime? I guess this is a php.ini thing.
Adding code: This code will output while executing in the first server, but not in the second server. Additional info, if i put the value of 1015 lower, the first server waits with outputting. So it seems that my first server is flushing every 1015 characters, while the second does not.
while ($a++ < 5) {
$b = 0;
while ($b++ < 1015) {
echo "H";
}
echo "<br><br>\n";
sleep(1);
}
On my second server, output buffering is off, and i tried turning on implicit_flush to no avail.
This is related to output buffering. If output buffering is turned on, the output is not sent to the client immediately but will wait until a certain amount of data buffered. This usually increases performance but will cause problem like yours.
The simple solution doesn't require changing any php.ini settings, but simply put this at the beginning of your script:
<?php
while (#ob_end_flush());
?>
This will flush and disable all output buffering at once.
(Source: http://www.php.net/manual/en/function.ob-end-flush.php#example-460)
You may also want to read the PHP manual on Output Control. The related php.ini settings are listed here.
If you want to make sure things are being sent (i.e. before a time-consuming task), call flush() at the appropriate times.
Edit:
In fact browsers may have their own buffers, especially when Content-Type is not explicitly set (the browser need to sniff the MIME-type of the file). If this is the case, you cannot control the browser's behaviour.
One way that might solve it is to explicitly set the Content-Type:
header("Content-Type: text/plain"); // if you don't need HTML
// OR
header("Content-Type: text/html");
But this is not guaranteed to work. It works on my Firefox but not Chrome.
Anyway, your final code should look like:
header("Content-Type: text/html"); // Set Content-Type
while (#ob_end_flush()); // Flush and disable output buffers
while ($a++ < 5) {
$b = 0;
while ($b++ < 1015) {
echo "H";
//flush(); // In fact because there is no significant delay, you should not flush here.
}
echo "<br><br>\n";
flush(); // Flush output
sleep(1);
}
Try calling flush periodically. If you are using output buffering, you may want to turn that off with a sequence of ob_end_flush or use ob_flush as well.

Using PHP Output Bufferering to Issue JavaScript During Processing

I have some PHP code that is receiving and processing large images. I'd like to echo out some JavaScript at certain points while the image is being processed to update the DOM with jQuery. Here is some sample code, but it isn't working. It just waits the entire 5 seconds and then makes the alerts happen back to back. I want it to do the first alert immediately and then next alert after 5 seconds.
ob_start();
echo '<script type="text/javascript">alert(\'1...\');</script>';
ob_flush();
sleep(5);
ob_start();
echo '<script type="text/javascript">alert(\'2...\');</script>';
ob_flush();
Can anyone help?
Most browsers buffer content until a certain size is reached. Try making your script blocks longer by padding them with something.
Also: You should call flush, not just ob_flush, and make sure zlib compression is turned off.
I have some PHP code that is receiving and processing large images. I'd like to echo out some JavaScript at certain points while the image is being processed to update the DOM with jQuery.
This may be out-of-scope for what you have to get done, but I'd use AJAX for this. You can certainly get what you want to occur, but the approach isn't good in the long term.
Instead of submitting the whole page and waiting for it to come back at a crawl, use an AJAX request to upload the image and get the result. Then a timer on the client can issue separate AJAX "how far done are you?" requests. The two PHP instances would communicate via setting a "done" flag on the job entry in a database, etc.
While it makes the client-side stuff a bit more complex, it is much easier to handle user interaction (such as allowing the user to cancel a long-running job) and makes your PHP code a lot more tightly-focused.
Adding this to the top of the script will work:
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);
As far as I know, ob_implicit_flush(1) forces a flush on every output statement. So the other ob_start() and ob_flush() calls wouldn't be necessary. I don't know if that works for you.
<?php
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);
echo '<script type="text/javascript">alert(\'1...\');</script>';
sleep(5);
echo '<script type="text/javascript">alert(\'2...\');</script>';
?>
Following is working in FF4:
<?php
echo '<script type="text/javascript">alert("1");</script>';
flush();
sleep(5);
echo '<script type="text/javascript">alert("2");</script>';
?>
I implemented a chat "server" with something like that long ago. It was working.
This ob_* stuff isn't helpful for this.

Printing results immediately (php)

I have a php script that connects 10 different servers to get data. I want it to print the results of the 1st connection before the second one begins.
Using flush and/or ob_flush, you should get what you want.
Here is a quick demonstration :
for ($i=0 ; $i<10 ; $i++) {
echo "$i<br />";
ob_flush();
flush();
sleep(1);
}
Each second, a number will be sent to the browser, without waiting for the loop/script to end.
(Without both flush and ob_flush, it waits until the end of the script to send the output)
Explanation about why you need both, quoting from the flush page in the manual :
Flushes the 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.
flush() may not be able to override
the buffering scheme of your web
server and it has no effect on any
client-side buffering in the browser.
It also doesn't affect PHP's userspace
output buffering mechanism. This means
you will have to call both ob_flush()
and flush() to flush the ob output
buffers if you are using those.
If this doesn't work for you, taking a look at the comments on the two pages of the manual can give you a couple of pointers on "why it could fail"
ob_end_flush
http://us.php.net/ob_end_flush
This function empties the output buffer and disables output buffering. Everything after this function is send to the browser immediately.
Yeah, ob_flush should do it. I do this all the time with a LOOONG page, when I want to watch the progress of the operation.

Categories