ob_start() without an ob_flush() - php

I hired someone to write an API for me in PHP and MySQL and now have to maintain it myself. I don't know php as well as other languages.
I noticed at the start of most of the php files they have:
ob_start();
I understand that this opens a new output buffer. The thing is that they never flush the buffer. The code had been working fine but I've had a lot of optimization issues, slow server responses, etc.
How is it that they don't have to flush the buffer but the response is still returning?
An example would be:
ob_start();
include "nusoap.php";
include "config.php";
require_once "class.Database.php";
$client = new nusoap_client($config['apiURL'].'/server.php',false, false, false, false, false, 600, 600);
... process the $_GET and build a $result ...
print_r($result);
Obviously the ... process ... is a wide open thing. But I'm not seeing anywhere in there that does any sort of flush or reading the ob contents. I've also searched all the includes and don't see one in there either.
I checked and implicit_flush is set to Off on this server. Since we did move this code from another server possibly on that server it was on. But still currently this API is working on this server.
The reason I said "not that I can find" when someone asked if there was an ob_get_contents() is because there are include files (including nusoap.php) that include other files and while I've grepped through them and tried to trace them, I might have missed something and am still searching. But so far it appears to my eye that no flush or get_contents is happening.
One possible answer is to say "NO, there has to either be an implicit_flush set in the php.ini file, or an implicit_flush() command somewhere, or another flush command somewhere, or getting the contents of the buffer somewhere - or the contents would never output." To me that is what the manual suggests. But sometimes there are loopholes and PHP seems to be a language of loopholes.
SOLVED
Indeed I did a simple test:
<?php
ob_start();
echo "Whats Up Doc!";
?>
and the output is seen in the browser.

PHP ob_start() function works by catching all output to buffer and then implicitly output this buffer on script end.
You may execute ob_end_clean() to discard (clean) buffer.
In Your example 'print_r($result);' will send output to buffer and then PHP interpreter will send buffer to client (http server/console).
PS. Function ob_implicit_flush() has different meaning. It just flush buffer on every output call (like print or echo), and do not have effect on script finish.

Related

Can I have the equivalent of ob_implicit_flush() and ob_end_flush() in php.ini?

When single-stepping through PHP code during debugging (e.g. in Netbeans) I want to see PHP output in my browser immediately. This can be achieved (at least when using PHP7.2 and Firefox) by including the following at the start of the PHP code:
ob_implicit_flush ();
while (#ob_end_flush());
This ensures that things like
echo "foo\n";
immediately result in 'foo' being displayed in the web browser rather than when the output buffer is due for a flush or the PHP code exits upon completion or error.
But is there a way to achieve this by editing php.ini so I don't have to include it in every bit of PHP code to be debugged? (I understand this will result in a performance penalty, but this is for development and debugging purposes only.)
Flushing the output buffer is an implicit action that the developer must make, so isn't something that can be toggled on or off in the php.ini.
However, PHP does have a feature called ticks, which allows the developer to register a callback function to be called every X lines of code that are executed.
See register_tick_function, where you can define your own callback function to perform the flush, and have it run in between every statement in your program. This will obviously kill performance, and there is probably a better way of achieving what you're trying to do.
Example:
declare(ticks=1);
function autoFlushBuffer() {
ob_implicit_flush ();
while (#ob_end_flush());
}
register_tick_function("autoFlushBuffer", true);
// The following lines will appear in your web browser one by one:
echo "one";
sleep(1);
echo "two";
sleep(1);
echo "three";
sleep(1);

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 doesn't detect connection abort at all

I have read and deeply understood these:
http://www.php.net/manual/en/features.connection-handling.php
http://www.php.net/manual/en/function.register-shutdown-function.php
However, I have tested both PHP 5.1.6 and 5.3 and things DON'T work as described there. What I observe is:
connection_status() always return true, even after the client has closed the connection.
execution of the script keeps going on after the client has closed the connection, even though ignore_user_abort is 0
a function registered with register_shutdown_function() is not run until the script reaches ends. The script is NOT interrupted (and hence the function not called) when the client aborts the connection.
So basically PHP just doesn't detect the client's disconnection AT ALL.
Note that this is NOT as if ignore_user_abort was set to 1: if that was the case then connection_status() would return 1 even though the script would keep running and the shutdown function would not be called until the end. That is not the case.
ini_get("ignore_user_abort") returns 0, as expected.
Is this a bug in PHP, or may this be due to some Apache setting?
How do I get PHP to work as described in the abovementioned documentation?
Test script:
<?php
function myShutdown() {
error_log("myShutdown ".connection_status()." ".ini_get("ignore_user_abort"));
}
register_shutdown_function(myShutdown);
echo "Hi!";
error_log(" *** test/test *** ");
for ($i=0; $i<10; $i++) {
sleep(1);
error_log(".");
echo ".";
}
?>
Steps to reproduce:
- visit the url of the script
- abort the connection on the client before 10 seconds have elapsed (e.g. hit the stop button in the browser)
Expected/Desired behavior:
The logs should show less than 10 dots, and at the end "myShutdown 1 0" (if you watch the log in real time, the myShutDown should appear immediately when the client disconnects)
Observed/current behavior:
The logs show always exactly 10 dots, and at the end "myShutdown 0 0" (if you watch it in realtime, it goes on for 10 seconds no matter when the client disconnects).
First, I also failed to get it to work, using the basic ubuntu 12.04 LAMP installation (php5.3). But I've some information and hope that it is helpful. Any comments or edits appreciated! :)
I see two problems with your code. The first is a syntax error. You are missing the single quotes around myShutdown when calling register_shutdown_function(). Change the line to:
register_shutdown_function('myShutdown');
The second problem I see is the missing flush() call after echos. The documentation says:
PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client. Simply using an echo statement does not guarantee that information is sent, see flush().
But even flush() will not help in any case. From the documentation of flush():
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.
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.
Even the browser may buffer its input before displaying it. Netscape, for example, buffers text until it receives an end-of-line or the beginning of a tag, and it won't render tables until the tag of the outermost table is seen.
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.
In the comments of the that page there is an advice to set several headers and apache configs:
apache_setenv('no-gzip', 1);
ini_set('zlib.output_compression', 0);
ini_set('implicit_flush', 1);
however, even this didn't work for me. I've investigated this using wiresharek, although the web server sends content ('Hi') after 0.0037 seconds, the web browser was buffering the page.

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

Can a PHP script trick the browser into thinking the HTTP request is over?

I first configure my script to run even after the HTTP request is over
ignore_user_abort(true);
then flush out some text.
echo "Thats all folks!";
flush();
Now how can I trick the browser into thinking the HTTP request is over? so I can continue doing my own work without the browser showing "page loading".
header(??) // something like this?
Here's how to do it. You tell the browser to read in the first N characters of output and then close the connection, while your script keeps running until it's done.
<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(true); // optional
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Will not work
flush(); // Unless both are called !
// At this point, the browser has closed connection to the web server
// Do processing here
echo('Text user will never see');
?>
Headers won't work (they're headers, so they come first)
I don't know of any way to close the http connection without terminating the script, though I suppose there's some obscure way of doing it.
Telling us what you want to do after the request is done would help us give better suggestions.
But generally, I'd be thinking about one of the following:
1) Execute some simple command-line script (using exec()) that looks like:
#!/bin/sh
php myscript.php <arg1> <arg2> .. <argN> &
Then kick that off from your http-bound script like:
<?PHP
exec('/path/to/my/script.sh');
?>
Or:
2) Write another program (possibly a continuously-running daemon, or just some script that is cronned ever so often), and figure out how your in-request code can pass it instructions. You could have a database table that queues work, or try to make it work with a flat file of some sort. You could also have your web-based script call some command-line command that causes your out-of-request script to queue some work.
At the end of the day, you don't want your script to keep executing after the http request. Assuming you're using mod_php, that means you'll be tying up an apache process until the script terminates.
Maybe this particular comment on php.net manual page will help: http://www.php.net/manual/en/features.connection-handling.php#71172
Theoretically, if HTTP 1.1 keep-alive is enabled and the client receives the amount of characters it expects from the server, it should treat it as the end of the response and go ahead and render the page (while keeping the connection still open.) Try sending these headers (if you can't enable them another way):
Connection: keep-alive
Content-Length: n
Where n is the amount of characters that you've sent in the response body (output buffering can help you count that.) I'm sorry that I don't have the time to test this out myself. I'm just throwing in the suggestion in case it works.
The best way to accomplish this is using output buffering. PHP sends the headers when it's good and ready, but if you wrap your output to the browser with ob_* you can control the headers every step of the way.
You can hold a rendered page in the buffer if you want and send headers till the sun comes up in china. This practice is why you may see a lot of opening <?php tags, but no closing tags nowadays. It keeps the script from sending any headers prematurely since there might some includes to consider.

Categories