PHP - echo before exec() - php

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.

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

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

Echo messages while php script still executes

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

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.

Can PHP tell when the browser goes away?

If I'm generating a stream of data to send out to a browser, and the user closes the browser, can I tell within PHP that I don't need to bother generating or sending the rest of the stream? I'd like to insert something into this loop:
while (!feof($pipes[1])) {
echo fgets($pipes[1]);
}
My fallback plan is to have the browser use a JavaScript onunload to hit another PHP page to kill the process that's generating the data, but it would be cleaner if PHP could tell when I'm echoing to nowhere.
By default PHP will abort the script if the user navigates away. There are however times where you don't want this to happen so php has a config you set called ignore_user_abort.
http://php.net/manual/en/misc.configuration.php
There's also a function called register_shutdown_function() that is supposedly executed when execution halts. I've never actually used it, so I won't vouch for how well it works, but I thought I'd mention it for completeness.
I believe that script will automatically abort when loaded normally (No ajax). But if you want to implement some sort of long polling via php using xmlhttprequest I think you will have to do it with some sort of javascript because then php can't detect it. Also like to know the precise case.
These answers pointed me towards what I was looking for. The underlying process needed special attention to kill it. I needed to jump out of the loop. Thanks again, Stack Overflow.
while (!feof($pipes[1]) && !connection_aborted())
{
echo fgets($pipes[1]);
}
if (connection_aborted())
{
exec('kill -4 '.$mypid);
}

Categories