php remove output buffering - php

I am doing a lot of processing at my PHP page and I want to display progress information to the user, for instance, finished 10% 20% and so. What is happening now is that all the data is displayed at once after processing is done, how can I display it right away!
I tried to set comment out output buffer in php.ini and I tried to use flush() after echo statements, not working, any suggestions?
Here is my code:
ob_start();
while ($line = read_file_line("c:/1.txt")) {
$read_lines_count++;
if($read_lines_count % 100 == 0) {
echo "parsed $read_lines_count <br />";
ob_flush();
}
}

First you need to call ob_start() before any code is printed.
Then echo whatever you want
call ob_flush() when you want to show the buffer on screen.
and at the end, a call to ob_end_flush() to end buffering and show output.
Make sure your php.ini has this line uncommented:
output_buffering = On
More Info : http://www.php.net/manual/en/book.outcontrol.php

Related

output before sleep using yii

So I used the following code to output content before calling the php function sleep():
ob_start();
echo "hello world";
ob_flush();
problem is that is not working using the yii framework. what should I do?
You should test, if there is already an output buffer open by writing this before your code:
if( ob_get_level () > 0 )
exit("That's why!");
My suggestion is, Yii or PHP itself has already opened an output buffer and you are just flushing it into the next level. If so, check the php.ini for output_buffering and of course the code before your code.
Thanks Oliver, your response helped me a lot.
I cleaned all levels in Yii2 to get it working
for ($level = ob_get_level(); $level > 0; --$level) {
if (!#ob_end_clean()) {
ob_clean();
}
}

How to determine whether ob_start(); has been called already

I use output buffering for gzip compression and access to what was put out before in a PHP script:
if(!ob_start("ob_gzhandler")) ob_start();
Now if that script gets included in another script where ob_start() already is in use I get a warning:
Warning: ob_start() [ref.outcontrol]: output handler 'ob_gzhandler' cannot be used twice in filename on line n
So I'd like to test whether ob_start() has already been called. I think ob_get_status() should be what I need but what is the best way to use it in testing for this?
ob_get_level returns the number of active output control handlers and ob_list_handlers returns a lift of those handlers. So you could do this:
if (!in_array('ob_gzhandler', ob_list_handlers())) {
ob_start('ob_gzhandler');
} else {
ob_start();
}
Although in general you can call ob_start any number of times you want, using ob_gzhandler as handler cannot as you would compress already compressed data.
if (ob_get_level())
echo "ob already started";
General:
if (ob_get_status()) {
// ob started
}
More specific
$status = ob_get_status();
if ($status['name']=='ob_gzhandler') {
// ob named ob_gzhandler started
}
What about using it this way?
if (ob_get_level() == 0) ob_start();

php - output echo'd contents in browser while script is running?

Does anyone know how to output stuff I echo to a browser while the script is running?
I have a long loop that I'd like to output a string after every run of the loop, kind of like a progress bar, does anyone know how to do that?
Use the ob_flush() function:
ob_start();
//echo stuff...
ob_flush();
//echo more stuff...
ob_flush();

How to flush output after each `echo` call?

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?
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 />';
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);
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);
}
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...
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.
what you want is the flush method.
example:
echo "log to client";
flush();
Why not make a function to echo, like this:
function fecho($string) {
echo $string;
ob_flush();
}
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.
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? .
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 );
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.
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";
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.
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
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;
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
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.
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();
}

PHP output to command line

I start my script from command line and it outputs things as they happen but a week ago it stopped outputing and now outputs everything when script finishes. I have ob_start() but as I know this does not effect command line output.
An easy way to do it is to create a function in php like this:
function console_log($message) {
$STDERR = fopen("php://stderr", "w");
fwrite($STDERR, "\n".$message."\n\n");
fclose($STDERR);
}
where $message is the desired output to command line. Then simply call the function wherever you would like to output and pass in whatever you want it to print.
You need to remove ob_start()... try this code on the command line, and it will print the text all at once:
<?
ob_start();
echo "test\n";
sleep(10);
echo "buffer\n";
?>
It'd be very helpful if you could post your script here, at least the relevant parts, but things I'd test are:
Did you turn on buffering?
Are you running the process in something like a nohup or something else that may be buffering it?
Did you change any other buffering settings?
Outputting only at the end of the script seems a buffering problem.

Categories