How to flush output after each `echo` call?
Asked Answered
D

21

78

I have a php script that only produces logs to the client.
When I echo something, I want it to be transferred to client on-the-fly.
(Because while the script is processing, the page is blank)
I had already played around with ob_start() and ob_flush(), but they didn't work.

What's the best solution?

PS: it is a little dirty to put a flush at the end of the echo call...

EDIT: Neither the Answers worked, PHP or Apache Fault?

Deaminate answered 28/6, 2010 at 14:26 Comment(3)
On the fly? You mean echo as it's being produced? Because output buffering functions like ob_start are there to do the exact opposite.Ockeghem
@Manos Dilaverakis - I mean, when i do an echo, i wan't it to be directly transfered to the client, for now, the page is loading, and only at the end of the script, it transfer all at once. Cat it be apache is fault?Deaminate
That is expected behaviour - output is sent once the script stops executing, unless you specify otherwise. Depending on how your application is designed, you may be able to flush the buffer at certain points in execution (for example when a class is instantiated or a given view function which is often called is run).Henrieta
D
45

Edit:

I was reading the comments on the manual page and came across a bug that states that ob_implicit_flush does not work and the following is a workaround for it:

ob_end_flush();

# CODE THAT NEEDS IMMEDIATE FLUSHING

ob_start();

If this does not work then what may even be happening is that the client does not receive the packet from the server until the server has built up enough characters to send what it considers a packet worth sending.


Old Answer:

You could use ob_implicit_flush which will tell output buffering to turn off buffering for a while:

ob_implicit_flush(true);

# CODE THAT NEEDS IMMEDIATE FLUSHING

ob_implicit_flush(false);
Dorado answered 28/6, 2010 at 14:36 Comment(10)
Yes, definitely I'm looking for something like that (+1), but it didn't worked :s Can it be Apache's fault?Deaminate
These two comments to that function may help: php.net/manual/en/function.ob-implicit-flush.php#35072 php.net/manual/en/function.ob-implicit-flush.php#33778Henrieta
If i stop the flush ob_end_flush(), my program skips the important step (don't know why ;s) I've added the line flush on my Class DebugEcho function, and it didn't workedtoo, besides, it's a little bad and time consuming doing a flush on the cache every echo call.Deaminate
ob_end_flush will clear the buffer and stop output buffering completely, so don't use that until you're sure that you don't want to buffer anymore content!Henrieta
You still need to flush() after each line of output as it is not guaranteed to be sent to the client immediately.Ibbison
You said that the server may not send the data until it has enough data collected. I wonder how to configure this size.Ms
@Dorado thanks! is it possible to flush on top of the previously flushed contents?Dahlgren
The above are rather old and not working. The 2018 version below is the only one working in 2018Scriber
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
The edit on this answer worked for me in 2020 combined with flush() and ob_flush().Mirilla
F
74

I've gotten the same issue and one of the posted example in the manual worked. A character set must be specified as one of the posters here already mentioned. http://www.php.net/manual/en/function.ob-flush.php#109314

header( 'Content-type: text/html; charset=utf-8' );
echo 'Begin ...<br />';
for( $i = 0 ; $i < 10 ; $i++ )
{
    echo $i . '<br />';
    ob_flush();
    flush();
    sleep(1);
}
echo 'End ...<br />';
Fatwitted answered 5/10, 2012 at 6:14 Comment(9)
I tried everything to get output-flushing working correctly. The thing that got it working in the end was "Content-type" header in your example. Thankyou fine sir!!!Bathesda
Yep, solved it for me as well. Awesome. Although, I also had to enable implicit flushing, but whatever. :)Cogan
None of the examples here and other pages worked for me.Linux with Apache and php 7Granny
I found that if the first echo is a tag, it seems to fail. Like echo '<h1>Begin...</h1>'; as the first echo would cause the entire thing to wait until it was finished. Using the exact example here worked fine on centos, apache, and php 7.Indeterminacy
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
Not working, tested with Firefox, Chrome, Edge, IE.Smyth
I improved this code a little by ob_flush()ing any explicit programmer induced caching first before trying to fully flush() to the browser. It is still possible for the Apache/web server to do its own caching that cannot be controlled by php code, or for the browser to cache contents before displaying it to you.Saintsimon
It works without the header and flush. You just need the ob_flush.Thistledown
In the beginning of this code, there must be: ob_start();Dogger
D
45

Edit:

I was reading the comments on the manual page and came across a bug that states that ob_implicit_flush does not work and the following is a workaround for it:

ob_end_flush();

# CODE THAT NEEDS IMMEDIATE FLUSHING

ob_start();

If this does not work then what may even be happening is that the client does not receive the packet from the server until the server has built up enough characters to send what it considers a packet worth sending.


Old Answer:

You could use ob_implicit_flush which will tell output buffering to turn off buffering for a while:

ob_implicit_flush(true);

# CODE THAT NEEDS IMMEDIATE FLUSHING

ob_implicit_flush(false);
Dorado answered 28/6, 2010 at 14:36 Comment(10)
Yes, definitely I'm looking for something like that (+1), but it didn't worked :s Can it be Apache's fault?Deaminate
These two comments to that function may help: php.net/manual/en/function.ob-implicit-flush.php#35072 php.net/manual/en/function.ob-implicit-flush.php#33778Henrieta
If i stop the flush ob_end_flush(), my program skips the important step (don't know why ;s) I've added the line flush on my Class DebugEcho function, and it didn't workedtoo, besides, it's a little bad and time consuming doing a flush on the cache every echo call.Deaminate
ob_end_flush will clear the buffer and stop output buffering completely, so don't use that until you're sure that you don't want to buffer anymore content!Henrieta
You still need to flush() after each line of output as it is not guaranteed to be sent to the client immediately.Ibbison
You said that the server may not send the data until it has enough data collected. I wonder how to configure this size.Ms
@Dorado thanks! is it possible to flush on top of the previously flushed contents?Dahlgren
The above are rather old and not working. The 2018 version below is the only one working in 2018Scriber
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
The edit on this answer worked for me in 2020 combined with flush() and ob_flush().Mirilla
V
45

So here's what I found out.

Flush would not work under Apache's mod_gzip or Nginx's gzip because, logically, it is gzipping the content, and to do that it must buffer content to gzip it. Any sort of web server gzipping would affect this. In short, at the server side we need to disable gzip and decrease the fastcgi buffer size. So:

  • In php.ini:

    output_buffering = Off
    
    zlib.output_compression = Off
    
  • In nginx.conf:

    gzip  off;
    
    proxy_buffering  off;
    

Also have these lines at hand, especially if you don't have access to php.ini:

@ini_set('zlib.output_compression',0);

@ini_set('implicit_flush',1);

@ob_end_clean();

set_time_limit(0);

Last, if you have it, comment the code bellow:

ob_start('ob_gzhandler');

ob_flush();

PHP test code:

ob_implicit_flush(1);

for ($i=0; $i<10; $i++) {
    echo $i;

    // this is to make the buffer achieve the minimum size in order to flush data
    echo str_repeat(' ',1024*64);

    sleep(1);
}
Variegation answered 12/2, 2011 at 15:19 Comment(7)
The key to the answer for me was the //this is for the buffer achieve the minimum size in order to flush data echo str_repeat(' ',1024*64);Emanation
Only this worked for me. +1 for the str_repeat bit, but perhaps a good idea to make it an invisible character instead of a whitespace?Naughty
So how do we disable gzip for that page only?Grainfield
echo str_repeat(' ',1024*64); made the flush work. But how come I couldn't find it anywhere else? The documentation doesn't mention it either....Bulky
The minimum buffer size is the critical missing piece. thanks!Soke
this is ultimate, god level steps to confirmed success. in "echo str_repeat(' ',1024*64);" i had to use 4096 instead of 1024 for better real time echo. cheersItagaki
This sorta works.. but having to send so many bytes defeats the purpose.Grainfield
K
20

For those coming in 2018:

The ONLY Solution worked for me:

<?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();
?>

and its very important to keep de "4096" part because it seems that "fills" the buffer...

Karl answered 21/2, 2018 at 20:23 Comment(8)
Not working great in 2020; the first 4 lines are displayed all in once after like 4 seconds, then after this, each new line is displayed one per sec.Berlyn
BUT if I increase the str_pad value, it's working better! with 4096000 its displaying pretty well. It's definitly a value of content handled by the browser.Berlyn
Isn't sending 4MB of text data in each step a bit overkill?Eatables
@Eatables yes it is! but it's the only way (I know) to do this using just php... the correct way should be ajax, but if you want use just php I don't know a better wayKarl
I use 4096 but did not work, change to 1024*64 working fine. I don't know why.Smyth
This worked for me without the sleep(2); line.Anemometry
You can also change the output_buffering = 4096 setting in php.ini to some lower value to avoid str_pad'ding to 4096.Ledoux
After a million tries, this is the ONLY code that worked for me with FastCGI. But using 4096 didn't work. I had to use str_pad('',1024*64);Luann
T
12

Flushing seemingly failing to work is a side effect of automatic character set detection.

The browser will not display anything until it knows the character set to display it in, and if you don't specify the character set, it need tries to guess it. The problem being that it can't make a good guess without enough data, which is why browsers seem to have this 1024 byte (or similar) buffer they need filled before displaying anything.

The solution is therefore to make sure the browser doesn't have to guess the character set.

If you're sending text, add a '; charset=utf-8' to its content type, and if it's HTML, add the character set to the appropriate meta tag.

Turbinal answered 24/4, 2012 at 21:19 Comment(1)
Doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
M
9

what you want is the flush method. example:

echo "log to client";
 flush();
Mellen answered 28/6, 2010 at 14:29 Comment(4)
@CuSS Regardless, this is the only way to satisfy the functionality that you wantIbbison
what's dirty about it? That's what flush() was designed to do, and it gets the job done.Mellen
ini_set('implicit_flush', true) will basically emulate a flush() after ever output block, and it can get set pretty much anywhere (php.ini, .htaccess, per-script, etc...)Clearstory
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
H
5

Why not make a function to echo, like this:

function fecho($string) {
 echo $string;
 ob_flush();
}
Henrieta answered 28/6, 2010 at 14:36 Comment(2)
Better that @Mellen Answer, but I'm searching for something like @Dorado Answer... You will get +1 ;)Deaminate
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
B
5

One thing that is not often mentionned is gzip compression that keeps turned ON because of details in various hosting environments.

Here is a modern approach, working with PHP-FPM as Fast CGI, which does not need .htaccess rewrite rule or environment variable :

In php.ini or .user.ini :

output_buffering = 0
zlib.output_compression = 0
implicit_flush = true
output_handler = 

In PHP script :

header('Content-Encoding: none'); // Disable gzip compression
ob_end_flush(); // Stop buffer
ob_implicit_flush(1); // Implicit flush at each output command

See this comment on official PHP doc for ob_end_flush() need.

Birgitbirgitta answered 18/1, 2019 at 14:13 Comment(3)
This is the only solution that worked for me, and I tried all. Thanks!Sachasachem
Not working great in 2020; the first 4 lines are displayed all in once after like 4 seconds, then after this, each new line is displayed one per secBerlyn
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
C
5
header( 'X-Accel-Buffering: no' );
header( 'Content-Type: text/html; charset=utf-8' );

echo 'text to display';
echo '<span style="display: none;">' . str_repeat ( ' ', 4096 ) . '</span>';
flush();
usleep( 10 );
Chretien answered 28/7, 2021 at 10:12 Comment(1)
Baam! Thank you so much for pointing out this X-Accel-Buffering Header!! After having tried nearly everything on my ionos webspace this really saved my day!! :)Chorus
P
4

I had a similar thing to do. Using

// ini_set("output_buffering", 0);  // off 
ini_set("zlib.output_compression", 0);  // off
ini_set("implicit_flush", 1);  // on   

did make the output flushing frequent in my case.

But I had to flush the output right at a particular point(in a loop that I run), so using both

ob_flush();
flush();

together worked for me.

I wasn't able to turn off "output_buffering" with ini_set(...), had to turn it directly in php.ini, phpinfo() shows its setting as "no value" when turned off, is that normal? .

Pathos answered 21/1, 2011 at 19:50 Comment(2)
Yes if phpinfo() shows "no value", it means that output_buffering is set to off. Otherwise it would say like 4096Moreira
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
I
3

The correct function to use is flush().

<html>
<body>
<p>
Hello! I am waiting for the next message...<br />
<?php flush(); sleep(5); ?>
I am the next message!<br />
<?php flush(); sleep(5); ?>
And I am the last message. Good bye.
</p>
</body>
</html>

Please note that there is a "problem" with IE, which only outputs the flushed content when it is at least 256 byte, so your first part of the page needs to be at least 256 byte.

Intubate answered 28/6, 2010 at 14:34 Comment(3)
Cool, didn't knew about that IE Bug... Is that any function besides echo and print that prints directly to the browser?Deaminate
@CuSS: It's not a bug; it's an "undocumented feature"Dorado
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
Q
3

This works fine for me (Apache 2.4/PHP 7.0):

@ob_end_clean();
echo "lorem ipsum...";
flush();
sleep(5);
echo "<br>dolor...";
flush();
sleep(5);
echo "<br>sit amet";
Quizmaster answered 27/6, 2018 at 15:52 Comment(1)
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
M
1

Anti-virus software may also be interfering with output flushing. In my case, Kaspersky Anti-Virus 2013 was holding data chunks before sending it to the browser, even though I was using an accepted solution.

Misuse answered 19/5, 2015 at 8:4 Comment(0)
A
0

Sometimes, the problem come from Apache settings. Apache can be set to gzip the output. In the file .htaccess you can add for instance :

SetEnv no-gzip 1
Alliber answered 21/10, 2013 at 9:37 Comment(0)
S
0

Try this:

while (@ob_end_flush());      
ob_implicit_flush(true);

echo "first line visible to the browser";
echo "<br />";

sleep(5);

echo "second line visible to the browser after 5 secs";

Just notice that this way you're actually disabling the output buffer for your current script. I guess you can reenable it with ob_start() (i'm not sure).

Important thing is that by disabling your output buffer like above, you will not be able to redirect your php script anymore using the header() function, because php can sent only once per script execution http headers. You can however redirect using javascript. Just let your php script echo following lines when it comes to that:

        echo '<script type="text/javascript">';
        echo 'window.location.href="'.$url.'";';
        echo '</script>';
        echo '<noscript>';
        echo '<meta http-equiv="refresh" content="0;url='.$url.'" />';
        echo '</noscript>'; 
        exit;
Salesgirl answered 19/9, 2014 at 16:49 Comment(1)
Code doesn't work. tested on awardspace free hosting default settings, firefox.Grainfield
C
0

Note if you are on certain shared hosting sites like Dreamhost you can't disable PHP output buffering at all without going through different routes:

Changing the output buffer cache If you are using PHP FastCGI, the PHP functions flush(), ob_flush(), and ob_implicit_flush() will not function as expected. By default, output is buffered at a higher level than PHP (specifically, by the Apache module mod_deflate which is similar in form/function to mod_gzip).

If you need unbuffered output, you must either use CGI (instead of FastCGI) or contact support to request that mod_deflate is disabled for your site.

https://help.dreamhost.com/hc/en-us/articles/214202188-PHP-overview

Crabstick answered 17/4, 2017 at 2:44 Comment(0)
M
0

I'm late to the discussion but I read that many people are saying appending flush(); at the end of each code looks dirty, and they are right.

Best solution is to disable deflate, gzip and all buffering from Apache, intermediate handlers and PHP. Then in your php.ini you should have:

            output_buffering = Off
            zlib.output_compression = Off
            implicit_flush = Off

Temporary solution is to have this in your php.ini IF you can solve your problem with flush(); but you think it is dirty and ugly to put it everywhere.

implicit_flush = On

If you only put it above in your php.ini, you don't need to put flush(); in your code anymore.

Moreira answered 6/8, 2017 at 14:1 Comment(0)
M
0

This is my code: (work for PHP7)

private function closeConnection()
{
    @apache_setenv('no-gzip', 1);
    @ini_set('zlib.output_compression', 0);
    @ini_set('implicit_flush', 1);

    ignore_user_abort(true);
    set_time_limit(0);

    ob_start();
    // do initial processing here
    echo json_encode(['ans' => true]);

    header('Connection: close');
    header('Content-Length: ' . ob_get_length());
    ob_end_flush();
    ob_flush();
    flush();
}
Magnetron answered 2/10, 2017 at 16:2 Comment(0)
J
0

https://mcmap.net/q/168825/-how-to-flush-output-after-each-echo-call

$input  = "Hello world";
echo str_pad($input,  1024*64, " ");

A lovely and practically sollution - with a a catch: A lot of recundant blanks in your output.

It produces in

  • Chrome + Edge: Produces a 10,005 character string ending with "…" as truncation
  • Firefox: A 65540 character string (sic)

Changing str_repeat to str_pad minimizes the issue. But changing the padding character from blank " " to null "\x00" seems the reduce the problem even further.

$input  = "Hello world";
echo str_pad($input,  1024*64, "\x00");

Produces in Chrome, Edge and Firefox a 15 character string

I cannot tell where the string is truncated. But the cache on the server is filled and the output is send AND your output is not cluttered by a menace of redundant blanks.

Jylland answered 20/2 at 10:12 Comment(0)
I
0

The situation is a little complex, but not hopeless, cause output will be buffered for different reasons, even at other stages, e.g. gzip, Apache, PHP (Fast)GCI vs. CLI (from commandline $ php -a) - and there are different browsers, too!

That's also why PHP did rate it as "NOT a bug", so more something like "rtfm", which still may be worth an improvement -- meaning, you should (must?) have a look at all related parts of the manual, not only at that for one single function?! I know, this might feel exhausting, but if you don't understand the system, also small differences and also small changes during time will make you fail - and the lot of "does not work for me"-comments show that.

There are often general chapters in the PHP manual, e.g. Output Buffering, which also tries to explain the differences between System and User-Level Buffers! A MUST READ!

Although having some specific remarks on a lot of answers and comments above, I'm afraid, I'm still not allowed to comment...

So just a few basic reminders from my "research" (learning) results, if at least you dig into your setting and have read most of the PHP manual:

Always have a look at your current settings, by php.ini and/or .htaccess and other parts; e.g. using <?php phpinfo(); ?> // but do not leave access to that info on a public/productive system!!

For example my default with PHP 8.2.17 says ... output_buffering = 4096

(spoiler: other versions of PHP may use integers instead of bools - always have a look at your version's manual - it's always good for a surprise)

  • setting output_buffering to a value will turn user-level buffering ON like a ob_start()
  • which I can not switch off at script-level via var_dump( ini_set('output_buffering', 0) ); - results in false indicating failure, can not be set
  • buffering is good for performance or for changing ("rewrite") and for discarding already produced output (think of a header for shipping an image in case of an error, needing a different header)
  • so to stop the buffering at a point in script, you may typically want a ob_end_flush() to send the current content, and end-ing buffering - not the 'clean' (discard buffer) version?
  • output buffering can have also more levels (on the user "ob_..." side)
  • the lowest (last frontier) is PHP's independent(!) system-level, controlled by flush()
  • and by the ob-vious misleading named ob_implicit_flush() (the default argument value is bool $enable = true), which flushes (the System Buffer!) automatically on every bit of code block
  • but we may still want a little buffering (for network and system performance?!) and would gain more output control with explicit use of flush()

Well, you are right, I should show something more:

Disclaimer: Using (standard) 'text/html' without s.th. like <!DOCTYPE html> will end up in "Quirks-Mode", what will provide another level of surprise - but do what you like ;)

<?php
header( 'Content-type: text/plain; charset=utf-8' );
//header( 'Content-type: text/html; charset=utf-8' );
//echo '<!DOCTYPE html>',PHP_EOL;

ob_end_flush(); // flush user buffer to deliver header data and end user-level buffering
ob_implicit_flush(true); // automatically flush system buffer on every code block

echo str_repeat(PHP_EOL, 5); // I like space

for( $i=10; $i>0; $i-- ){
  echo $i,'  ';
  //flush(); // now done by _implicit_ 
  sleep(1);
}
echo '*',PHP_EOL;
// final flushing always done by end of script
?>

Works for me :) well, even not obvious, so wish you good luck and endurance! Live long and prosper \\//- BRs Klaus

Insulin answered 3/4 at 18:59 Comment(0)
C
0

Best solution for me (and hope for you):

At the beginning of php:

header('Content-Encoding: none');
ob_end_flush();
ob_implicit_flush(1);

Then, only this in your loop:

echo str_pad('',4096);

Example:

<?php

  header('Content-Encoding: none');
  ob_end_flush();
  ob_implicit_flush(true);

  for($i = 0; $i < 100;$i++){
    $txt = "Number is: " . $i . "<br />";
    echo str_pad($txt,4096);
    sleep(1);
  }

?>
Clayborn answered 12/4 at 20:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.