PHP output buffering? What's the best practise? - php

Further to my previous question, what's the best approach when I want to buffer PHP output until I have performed all processing? I want to buffer to leave myself the option to to redirect to an error page, which I can't do after any output.
So, what's the best practise? Use a variable $output and keep appending to it, then output it at the end? Or use ob_str(), etc?
Is there a peformance to choose code-maintainability reason for one over the other? Or is it just personal preferance?

For me, I did this:
<?php
ob_start();
//do your process here
if($error)
{
ob_end_clean();
header('Location: /some/path.php');
exit;
}
ob_end_flush();
?>

I open a buffer with ob_start(); ( http://php.net/manual/en/function.ob-start.php )
Then anything that would normally be sent to the browser (except headers) is stored in the buffer until I close it. When I want to output or manipulate the buffer, I access it like this:
$buffer = ob_get_clean(); ( http://php.net/manual/en/function.ob-get-clean.php )
There are lots of other buffer options here:
http://www.php.net/manual/en/ref.outcontrol.php
This is the best way in my opinion because you don't have to keep adding items to the buffer; PHP is automatically capturing everything as long as the buffer is open.

Well written code needs no output buffering. By that I mean: first, you do all your processing, without any output. Business logic, validation, database access - this kind of stuff. After this is done, you can close the DB connection, the session, etc. because all you do is create your output based on data collected above.
This method usually results in far better maintainable code.

Related

PHP - echo before exec()

Good day!
I am having some issues with getting the echo statement to output before the execution of the exec()
<?
if (isset($_POST['ipaddress'])) {
$escaped_command = escapeshellcmd($_POST['ipaddress']);
if(filter_var($escaped_command, FILTER_VALIDATE_IP)) {
echo "Gleaning ARP information, please wait..";
$command = exec('sudo /sbin/getarp.exp');
The echo statement is being outputted after the execution of the $command. The execution time can be anywhere from 15-30 seconds depending on how large the ARP table on the remote router is. Is there an order of operations that I am not aware of? It appears that all the statements within the if statement are executed in parallel and not by line by line as I had assumed.
I would rather not a solution be provided, but some documentational links that would lead me to finding a solution. I have searched what I could, but was not able to find a viable solution.
Any help would be appreciated.
Thanks.
This is happening because the script will run in its entirety before any result/output is sent to the browser.
In PHP there is a concept of "output buffering".
Whenever you output something (e.g. using echo, print, etc.) the text is thrown into a buffer. This buffer is only sent at certain times (at the end of the request, for instance, or when the buffer is full).
In order to empty the buffer (to "flush" it) you need to do it manually. The flush() function will do this. Sometimes you also need to call ob_flush() (this is if you have opened custom output buffers yourself). It is generally a good idea to just call both functions and be done with it:
echo 'Wait a few seconds...';
flush(); ob_flush();
sleep(3);
echo ' aaand we are done!';
See Output Buffering Control for more information on output buffering in PHP.
This is probably an issue with the output buffer. PHP buffers output and writes it to the browser in chunks. Try adding a call to ob_flush() between the echo and the exec(); this will force PHP to write the current contents of the buffer to the browser.
By default, php does not send any of the output until the php script is done running completely. There is a solution. However, I hear it is a little browser dependent. I would test it on different systems and browsers to see if it is working:
ob_implicit_flush (true)
Put that before any of your echo/print commands and that should allow anything printed to show right up on the browser.
A more universal approach would be to integrate your page with asynchronous javascript. A process commonly referred to as "AJAX". It is a little more difficult because it requires the use of many interacting scripts, some client-side and some server-side. However, AJAX is the defacto way to do thing like this on the web.

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.

Fire-and-forget in PHP

Final update
Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D
I'm trying to achieve fire-and-forget like functionality in PHP.
From php.net
<?php
ignore_user_abort(true);
header("Content-Length: 4");
header("Connection: Close");
echo "abcd";
flush();
sleep(5);
echo "Text user should not see"; // because it should have terminated
?>
This works if I open the script with a browser. (shows "abcd").
But if I open it with file_get_contents or some stream library it will wait for ~5 seconds and show the second text as well.
I'm using PHP 5.2.11 / Apache 2.0
Update
I seems there is some confusion about what I'm trying to accomplish.
I don't want to hide output using output buffers (that's stupid). I want to have the client terminate before the server starts a possibly lengthy process (sleep(5)) and I don't want the client to wait for it (this is what fire-and-forget means, sort off).
The use of output buffers is merely a side effect. I've amended the sample code without the use of output buffers.
What I don't understand is: why does this script behave differently when accessing it from the browser vs. fetching it in PHP with file_get_contents("http://dev/test.php") or some stream library? What I've seen in testing is that for instance stream_get_contents will actually block for 5 seconds before it returns any output at all, the is quite the opposite of what I want.
Update2
Some more results:
The browser somehow responds to the flush(). I can't figure out how to replicate this behavior with streams in PHP, my streams keep blocking.
I've tried fread and found that it behaves similar to stream_get_contents.
Specifying a maxlength has no effect, it will still block for ~5 seconds.
Changing the blocking mode has no effect (other than generating a bunch more calls to stream_get_contents()). It will wait ~5 seconds before returning anything.
stream_set_read_buffer has no effect (tested on a PHP 5.3.5 sever)
The second portion of text is showing up because you're stopping output buffering with ob_end_flush() and ob_end_clean(). When that happens PHP outputs content as normal. Try something like the following:
<?php
ob_start(); // turn on output buffering
print "Text the user will see.";
ob_flush(); // send above output to the user and keep output buffering on
print "Text the user will never see";
ob_end_clean(); // empty the buffer and turn off output buffering. your script should end here.
?>
It's important for ob_end_clean() to appear at the end of the script. It empties the buffer and does not send its contents to the user, thus keeping everything after ob_flush() hidden.
How do you access the script using file_get_contents? How do you access it with your browser? If you access the script without "http://", of course it will never get executed. Use the same URL as in the browser.
Edit:
The browser will render the page even before the connection is closed. Even if you flush, I don't think the connection is closed. You can fire up Wireshark and check. stream_get_contents and file_get_contents will block until they have all the output. Even if you flushed, they can't be sure that there isn't more content. Since the content-length header didn't seem to make {file,stream}_get_contents return earlier, you probably need to implement your own buffering, ala. fopen, read, fclose.
Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D

what is the role of ob_start() in here

session_start();
ob_start();
$hasDB = false;
$server = 'localhost';
$user = 'user';
$pass = 'pass';
$db = 'acl_test';
$link = mysql_connect($server,$user,$pass);
if (!is_resource($link)) {
$hasDB = false;
die("Could not connect to the MySQL server at localhost.");
} else {
$hasDB = true;
mysql_select_db($db);
}
a) what does ob_start() exactly do.? i got to understand it will turn output buffering on. with reference to the above code what will be the benefit if i use ob_start() while trying to establish the connection with the database. what output data it will buffer?
thank you..
Normally php sends all text not included in <?php ... ?>, all echos, all prints to the output. Which is send to the err... output: http server (which sends it to a client), console etc.
After ob_start this output is saved in output buffer, so you can later decide what to do with it.
It doesn't affect db connection. It deals with text (mostly) produced by php.
Some PHP programmers put ob_start() on the first line of all of their code*, and I'm pretty certain that's what going on here.
It means if they get halfway through outputting the page and decide there's an error, they can clear the buffer and output an error page instead. It also means you never get "couldn't sent headers, output already started" errors when trying to send HTTP headers.
There are a few legitimate reasons to do this, but I'd take it as a sign that they are mediocre programmers who don't want to structure their code in a coherent order - i.e. they should be working out if there are errors or headers to send before they start rendering the page. Don't copy this habit.
(* What makes this an easy habit to fall into is that if output buffering is still turned on when the script end is reached, the buffer is sent to the user, so they don't need to worry about a matching end statement)
first of all buffering is usefull for putting http-headers (header function) at any line of code. eg - session cookies. without ob_start you would not be able to add any http-header to response if you already sent some data, e.g. with echo or print functions
In this particular code ob_start seems useless. The benefit of output buffering is in either following code or does not exist. `In general, consult PHP manual about ob_start.

Return value to browser but still process in PHP

I am using flash to call a PHP page that needs to do a bit of processing. Is it possible to let PHP continue processing but show a response anyway so flash doesn't stall waiting?
My answer from here:
You can send Connection:Close headers,
which finishes the page for your user,
but enables you to execute things
"after page loads".
There is a simple way to ignore user
abort (see php manual too):
ignore_user_abort(true);
Use output control aka output buffering to do this. http://www.php.net/manual/en/function.ob-flush.php
You could try using flush()
As an example, try these two different pieces of code:
// without flush()
foreach ( range(1, 5) as $num ) {
echo "Beep $num<br>";
sleep(1);
}
// with flush()
foreach ( range(1, 5) as $num ) {
echo "Beep $num<br>";
flush();
sleep(1);
}
You can close the connection within a registered function within register_shutdown_function if you do not need to wait for the processing to be over to output content (i.e., if you do not need to output anything related to the outcome of the processing you wish to do).
See : http://www.php.net/manual/en/features.connection-handling.php#93441
The reason to put it in a register_shutdown_function is that even if the client aborts the connection, the processing will keep on going to the very end.

Categories