I want PHP to output while it's still running [duplicate] - php

I'm trying to run a loop every second for 25 seconds basically.
for($i = 0; $i <= 25; $i += 1){
echo $i;
sleep(1)
}
The thing is it doesn't output until it's fully done, so after the loop continues 25 times. Is there a way to do this so it will output before each sleep? and not wait until the full loop is complete?
Thanks!

I just hashed through this same problem from a beginner perspective and came up with this bare-bones script which will do what you want.
<?PHP
ob_start();
$buffer = str_repeat(" ", 4096)."\r\n<span></span>\r\n";
for ($i=0; $i<25; $i++) {
echo $buffer.$i;
ob_flush();
flush();
sleep(1);
}
ob_end_flush();
?>
Questions that you may ask could be here (about \r\n) and here (about ob_flush()). Hope that helps you out.

What you're trying to achieve is incremental output to the browser from PHP.
Whether this is achievable can depend on your server and how you're invoking PHP.
PHP under FastCGI
You're probably a bit more likely to run into this kind of problem when PHP is running under FastCGI rather than as an Apache module, because the coupling between the server and the PHP processes is not as tightly coupled. FastCGI communication uses output buffering once the data has left the PHP processes, with the output sent to the browser only once the request is fully complete, or this buffer has filled up. On top of this, the PHP processes tend to be terminated after a certain amount of time, to avoid letting any one run for too long.
That said, a combination of ob_end_flush() (or ob_flush()) and flush() should still cause PHP to request that the downstream buffers are cleared, so this may still work. You may need to also investigate whether you need to length the time limit for PHP scripts.
PHP under mod_php
If you're using mod_php, you can write incrementally out to the browser. Use the flush() command to ensure that the PHP module will flush it instantly. If you don't have output buffering, or some Apache module such as mod_gzip, then it should go out instantly to the user's browser. What's more, you can keep your PHP script running as long as you like (with set_time_limit() in PHP), under the default configurations, though of course it will consume some memory.
You may run into trouble with some browsers which don't start rendering the page until a certain amount of a page is downloaded. Some versions of IE may wait for 1KB. I've found that Chrome can wait for more. A lot of people get around this by adding padding, such as a long comment 1 or 2 KB long at the top of the document.

Call flush will force PHP to push all of the output buffer to the client before proceeding.
for($i = 0; $i <= 25; $i += 1){
echo $i;
flush();
sleep(1);
}
EDIT:
After testing this on my lighttpd server I noticed that it buffered my outputs in blocks of 4096 characters, and I assume other browser might have similar buffering schemes. Also GZIP can prevent flush completely. Unfortunately there is no way to test that it's working due to the nature of HTTP.
Also another issue with this strategy is that it leaves that PHP proc blocked to other requests. This can cause requests to pile up.

Related

PHP FastCGI Simple Counter

I am unable to understand and run a simple PHP script in FCGI mode. I am learning both Perl and PHP and I got the Perl version of FastCGI example below to work as expected.
Perl FastCGI counter:
#!/usr/bin/perl
use FCGI;
$count = 0;
while (FCGI::accept() >= 0) {
print("Content-type: text/html\r\n\r\n",
"<title>FastCGI Hello! (Perl)</title>\n",
"<h1>FastCGI Hello! (Perl)</h1>\n",
"Request number ", $++count,
" running on host <i>$ENV('SERVER_NAME')</i>");
}
Searching for similar in PHP found talk about "fastcgi_finish_request" but have no clue how
to accomplish the counter example in PHP, here is what I tried:
<?php
header("content-type: text/html");
$counter++;
echo "Counter: $counter ";
//http://www.php.net/manual/en/intro.fpm.php
fastcgi_finish_request(); //If you remove this line, then you will see that the browser has to wait 5 seconds
sleep(5);
?>
Perl is not PHP. This must not mean that you can not most often interchange things and port code between the two, however when it comes to runtime environments there are bigger differences you can not just interchange.
FCGI is on the request / protocol level already which is fully abstracted in the PHP runtime and you therefore have not as much control in PHP as you would have with Perl and use FCGI;
Therefore you can not just port that code.
Next to that fastcgi_finish_request is totally unrelated to the Perl code. You must have confused it or thrown it in by sheer luck to give it a try. However it's not really useful in this counter example context.
PHP and HTTP are stateless.
All data is only relevant for the current, ongoing request.
If you need to save state, you might consider storing the data into cookie, session, cache or db.
So the implementation of this "counter" example will be different for PERL and PHP.
Your usage of fastcgi_finish_request won't bring the functionality you expect from PERL.
Think about a long running calculation, where you output data in the middle.
You can do that with fastcgi_finish_request, the data is then pushed to the browsers, while the long running tasks keeps running.
Opening happens together FASTCGI+PHP.
Normally the connection would be open till PHP finishes, then FASTCGI would be closed.
Except you reach the exec timeout of PHP (exec timeout) or fastcgi timeout (connection timeout). fastcgi_finish_request handles the case, where the fascgi connection to the browser is closed BEFORE PHP finishes execution.
Simple Hit Counter Example for PHP
<?php
$hit_count = #file_get_contents('count.txt'); // read count from file
$hit_count++; // increment hit count by 1
echo $hit_count; // display
#file_put_contents('count.txt', $hit_count); // store the new hit count
?>
Honestly, that's not even how you should do it using Perl either.
Instead, I'd recommend using CGI::Session to track session information:
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use CGI::Session;
my $q = CGI->new;
my $session = CGI::Session->new($q) or die CGI->Session->errstr;
print $session->header();
# Page View Count
my $count = 1 + ($session->param('count') // 0);
$session->param('count' => $count);
# HTML
print qq{<html>
<head><title>Hello! (Perl)</title></head>
<body>
<h1>Hello! (Perl)</h1>
<p>Request number $count running on host <i>$ENV{SERVER_NAME}</i></p>
</body>
</html>};
Alternatively, if you really want to go barebones, you could keep a local file as demonstrated in: I still don't get locking. I just want to increment the number in the file. How can I do this?

Multiple flush statements in PHP and Apache

I' am using XMLHttpRequest 2 to get chunked response through it's onprogress event and displaying it in browser. Everything seems to work fine but I have a little confusion. Consider the following code:
<?php
echo "I' am sending ";
ob_flush();
flush();
echo "content to client.";
ob_flush();
flush();
This is pretty standard PHP way of sending chunked response to client. However I was expecting to get output as two different chunks, one containing first echo's result and other containing second's. But I' am getting a single combined chunk of both echo statements. When I put sleep(1) after first flush() then everything seems to work fine.
My question is that is there some deliberate wait on webserver side to wait for some other content before sending already received chunk or is there some thread scheduling mechanism that is forcing network layer of Apache to wait for it's turn.
Edit
I forgot to mention that I have disabled webserver's output buffering which means if I flush something from PHP it will get sent to browser.
Any help is highly appreciated.
Thanks.
See: http://www.icemelon.com/tutorials/20/Output_While_Script_Still_Running.htm
You may be seeing browser output buffering here. Try adding:
for($k = 0; $k < 40000; $k++)
echo ' ';
Right before you flush in order to fill up the browser's buffer and make it spit out your output.

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.

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

Categories